mglon-schedule 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3800 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, input, computed, inject, ElementRef, PLATFORM_ID, output, signal, HostListener, Input, Directive, viewChild, afterNextRender, contentChildren, effect, Renderer2, ViewContainerRef, EventEmitter, Output, ViewEncapsulation, InjectionToken, untracked } from '@angular/core';
3
+ import { trigger, transition, style, animate } from '@angular/animations';
4
+ import { startOfMonth, endOfMonth, startOfWeek, endOfWeek, eachDayOfInterval, isSameMonth, startOfDay, endOfDay, differenceInCalendarDays, addDays, differenceInDays, max, min, differenceInMilliseconds, addMilliseconds } from 'date-fns';
5
+ import * as i1 from '@angular/common';
6
+ import { CommonModule, isPlatformBrowser } from '@angular/common';
7
+ import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
8
+ import { Subject } from 'rxjs';
9
+ import { DomSanitizer } from '@angular/platform-browser';
10
+ import { filter, takeUntil } from 'rxjs/operators';
11
+ import { RRule } from 'rrule';
12
+
13
+ class ResourceView {
14
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResourceView, deps: [], target: i0.ɵɵFactoryTarget.Component });
15
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: ResourceView, isStandalone: true, selector: "mglon-resource-view", ngImport: i0, template: "<div class=\"ngx-cal-resource-view\">\n <!-- Header: Time/Dates (Sticky Top) -->\n <div class=\"ngx-cal-resource-view__header\">\n <div class=\"ngx-cal-resource-view__corner\"></div> <!-- Empty corner above sidebar -->\n <div class=\"ngx-cal-resource-view__time-track\">\n <!-- Mock items for now -->\n <div class=\"ngx-cal-resource-view__time-cell\">08:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">09:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">10:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">11:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">12:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">13:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">14:00</div>\n </div>\n </div>\n\n <!-- Body: Sidebar + Grid -->\n <div class=\"ngx-cal-resource-view__body\">\n <!-- Sidebar: Resources (Sticky Left) -->\n <div class=\"ngx-cal-resource-view__sidebar\">\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource A</div>\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource B</div>\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource C</div>\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource D</div>\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource E</div>\n </div>\n\n <!-- Main Grid -->\n <div class=\"ngx-cal-resource-view__grid\">\n <!-- Rows corresponding to resources -->\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n </div>\n </div>\n</div>", styles: [":host{--mglon-schedule-primary-50: #e8f0fe;--mglon-schedule-primary-100: #d2e3fc;--mglon-schedule-primary-200: #a7cbfb;--mglon-schedule-primary-300: #7cacf8;--mglon-schedule-primary-400: #518ef5;--mglon-schedule-primary-500: #1a73e8;--mglon-schedule-primary-600: #1967d2;--mglon-schedule-primary-700: #185abc;--mglon-schedule-primary-800: #174ea6;--mglon-schedule-primary-900: #0d3a86;--mglon-schedule-primary-950: #08265a;--mglon-schedule-neutral-50: #fafafa;--mglon-schedule-neutral-100: #f5f5f5;--mglon-schedule-neutral-200: #e5e5e5;--mglon-schedule-neutral-300: #d4d4d4;--mglon-schedule-neutral-400: #a3a3a3;--mglon-schedule-neutral-500: #737373;--mglon-schedule-neutral-600: #525252;--mglon-schedule-neutral-700: #404040;--mglon-schedule-neutral-800: #262626;--mglon-schedule-neutral-900: #171717;--mglon-schedule-neutral-950: #0a0a0a;--mglon-schedule-primary: var(--mglon-schedule-primary-500);--mglon-schedule-on-primary: #ffffff;--mglon-schedule-surface: #ffffff;--mglon-schedule-on-surface: var(--mglon-schedule-neutral-900);--mglon-schedule-surface-variant: var(--mglon-schedule-neutral-100);--mglon-schedule-on-surface-variant: var(--mglon-schedule-neutral-700);--mglon-schedule-background: #ffffff;--mglon-schedule-on-background: var(--mglon-schedule-neutral-900);--mglon-schedule-border: var(--mglon-schedule-neutral-200);--mglon-schedule-text-primary: var(--mglon-schedule-neutral-900);--mglon-schedule-text-secondary: var(--mglon-schedule-neutral-500);--mglon-schedule-text-tertiary: var(--mglon-schedule-neutral-400);--mglon-schedule-hover: var(--mglon-schedule-neutral-100);--mglon-schedule-selection: var(--mglon-schedule-primary-50);--mglon-schedule-error: #ea4335;--mglon-schedule-on-error: #ffffff;--mglon-schedule-spacing-xs: 4px;--mglon-schedule-spacing-sm: 8px;--mglon-schedule-spacing-md: 16px;--mglon-schedule-spacing-lg: 24px;--mglon-schedule-padding-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-padding-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-padding-md: var(--mglon-schedule-spacing-md);--mglon-schedule-padding-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-margin-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-margin-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-margin-md: var(--mglon-schedule-spacing-md);--mglon-schedule-margin-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-gap-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-gap-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-gap-md: var(--mglon-schedule-spacing-md);--mglon-schedule-gap-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-border-width-sm: 1px;--mglon-schedule-border-width-md: 2px;--mglon-schedule-border-width-lg: 4px;--mglon-schedule-header-height: 48px;--mglon-schedule-sidebar-width: 60px;--mglon-schedule-cell-height: 48px;--mglon-schedule-radius-sm: 4px;--mglon-schedule-radius-md: 8px;--mglon-schedule-radius-lg: 16px;--mglon-schedule-radius-full: 9999px;--mglon-schedule-font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;--mglon-schedule-font-size-xs: 10px;--mglon-schedule-font-size-sm: 12px;--mglon-schedule-font-size-md: 14px;--mglon-schedule-font-size-lg: 16px;--mglon-schedule-shadow-sm: 0 1px 2px rgba(0, 0, 0, .1);--mglon-schedule-shadow-md: 0 2px 4px rgba(0, 0, 0, .15);--mglon-schedule-shadow-lg: 0 4px 8px rgba(0, 0, 0, .2);--mglon-schedule-z-sticky: 10;--mglon-schedule-z-header: 20;--mglon-schedule-hover-bg: rgba(0, 0, 0, .04);--mglon-schedule-focus-ring-color: var(--mglon-schedule-primary-200);--mglon-schedule-disabled-opacity: .5;--mglon-schedule-transition-duration: .2s;--mglon-schedule-transition-easing: cubic-bezier(.4, 0, .2, 1);--mglon-schedule-month-week-display: grid;--mglon-schedule-month-week-columns: repeat(7, 1fr);--mglon-schedule-month-week-min-height: 80px;--mglon-schedule-month-week-border-bottom-width: 1px;display:block;--resource-view-bg: var(--mglon-resource-view-bg, var(--mglon-schedule-background));--resource-view-font-family: var(--mglon-resource-view-font-family, var(--mglon-schedule-font-family));--resource-view-text-color: var(--mglon-resource-view-text-color, var(--mglon-schedule-text-primary));--resource-sidebar-width: var(--mglon-resource-sidebar-width, var(--mglon-schedule-sidebar-width));--resource-header-height: var(--mglon-resource-header-height, var(--mglon-schedule-header-height));--resource-cell-height: var(--mglon-resource-cell-height, var(--mglon-schedule-cell-height));--resource-border: var(--mglon-resource-border, 1px solid var(--mglon-schedule-border));font-family:var(--resource-view-font-family);color:var(--resource-view-text-color);background-color:var(--resource-view-bg);height:100%;overflow:auto;position:relative}.ngx-cal-resource-view{display:grid;grid-template-columns:var(--resource-sidebar-width) 1fr;grid-template-rows:var(--resource-header-height) 1fr;min-width:fit-content}.ngx-cal-resource-view__header{display:contents}.ngx-cal-resource-view__corner{top:0;z-index:var(--mglon-schedule-z-header);position:sticky;left:0;z-index:var(--mglon-schedule-z-sticky);z-index:30;grid-row:1;grid-column:1;border-right:var(--resource-border);border-bottom:var(--resource-border);background-color:var(--mglon-schedule-surface)}.ngx-cal-resource-view__time-track{position:sticky;top:0;z-index:var(--mglon-schedule-z-header);grid-row:1;grid-column:2;display:flex;border-bottom:var(--resource-border);background-color:var(--mglon-schedule-surface)}.ngx-cal-resource-view__time-cell{display:flex;align-items:center;justify-content:center;flex:0 0 100px;height:100%;border-right:var(--resource-border);font-size:var(--mglon-schedule-font-size-sm);color:var(--mglon-schedule-text-secondary)}.ngx-cal-resource-view__sidebar{grid-row:2;grid-column:1;display:block;background-color:var(--mglon-schedule-surface)}.ngx-cal-resource-view__resource-cell{position:sticky;left:0;z-index:var(--mglon-schedule-z-sticky);height:var(--resource-cell-height);display:flex;align-items:center;padding:0 var(--mglon-schedule-spacing-sm);border-right:var(--resource-border);border-bottom:var(--resource-border);font-size:var(--mglon-schedule-font-size-md);background-color:var(--mglon-schedule-surface);width:var(--resource-sidebar-width);box-sizing:border-box;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ngx-cal-resource-view__grid{grid-row:2;grid-column:2;display:block}.ngx-cal-resource-view__row{display:flex;height:var(--resource-cell-height);border-bottom:var(--resource-border)}.ngx-cal-resource-view__cell{flex:0 0 100px;border-right:var(--resource-border);height:100%}.ngx-cal-resource-view__cell:hover{background-color:var(--mglon-schedule-hover)}\n"] });
16
+ }
17
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResourceView, decorators: [{
18
+ type: Component,
19
+ args: [{ selector: 'mglon-resource-view', imports: [], template: "<div class=\"ngx-cal-resource-view\">\n <!-- Header: Time/Dates (Sticky Top) -->\n <div class=\"ngx-cal-resource-view__header\">\n <div class=\"ngx-cal-resource-view__corner\"></div> <!-- Empty corner above sidebar -->\n <div class=\"ngx-cal-resource-view__time-track\">\n <!-- Mock items for now -->\n <div class=\"ngx-cal-resource-view__time-cell\">08:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">09:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">10:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">11:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">12:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">13:00</div>\n <div class=\"ngx-cal-resource-view__time-cell\">14:00</div>\n </div>\n </div>\n\n <!-- Body: Sidebar + Grid -->\n <div class=\"ngx-cal-resource-view__body\">\n <!-- Sidebar: Resources (Sticky Left) -->\n <div class=\"ngx-cal-resource-view__sidebar\">\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource A</div>\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource B</div>\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource C</div>\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource D</div>\n <div class=\"ngx-cal-resource-view__resource-cell\">Resource E</div>\n </div>\n\n <!-- Main Grid -->\n <div class=\"ngx-cal-resource-view__grid\">\n <!-- Rows corresponding to resources -->\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n <div class=\"ngx-cal-resource-view__row\">\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n <div class=\"ngx-cal-resource-view__cell\"></div>\n </div>\n </div>\n </div>\n</div>", styles: [":host{--mglon-schedule-primary-50: #e8f0fe;--mglon-schedule-primary-100: #d2e3fc;--mglon-schedule-primary-200: #a7cbfb;--mglon-schedule-primary-300: #7cacf8;--mglon-schedule-primary-400: #518ef5;--mglon-schedule-primary-500: #1a73e8;--mglon-schedule-primary-600: #1967d2;--mglon-schedule-primary-700: #185abc;--mglon-schedule-primary-800: #174ea6;--mglon-schedule-primary-900: #0d3a86;--mglon-schedule-primary-950: #08265a;--mglon-schedule-neutral-50: #fafafa;--mglon-schedule-neutral-100: #f5f5f5;--mglon-schedule-neutral-200: #e5e5e5;--mglon-schedule-neutral-300: #d4d4d4;--mglon-schedule-neutral-400: #a3a3a3;--mglon-schedule-neutral-500: #737373;--mglon-schedule-neutral-600: #525252;--mglon-schedule-neutral-700: #404040;--mglon-schedule-neutral-800: #262626;--mglon-schedule-neutral-900: #171717;--mglon-schedule-neutral-950: #0a0a0a;--mglon-schedule-primary: var(--mglon-schedule-primary-500);--mglon-schedule-on-primary: #ffffff;--mglon-schedule-surface: #ffffff;--mglon-schedule-on-surface: var(--mglon-schedule-neutral-900);--mglon-schedule-surface-variant: var(--mglon-schedule-neutral-100);--mglon-schedule-on-surface-variant: var(--mglon-schedule-neutral-700);--mglon-schedule-background: #ffffff;--mglon-schedule-on-background: var(--mglon-schedule-neutral-900);--mglon-schedule-border: var(--mglon-schedule-neutral-200);--mglon-schedule-text-primary: var(--mglon-schedule-neutral-900);--mglon-schedule-text-secondary: var(--mglon-schedule-neutral-500);--mglon-schedule-text-tertiary: var(--mglon-schedule-neutral-400);--mglon-schedule-hover: var(--mglon-schedule-neutral-100);--mglon-schedule-selection: var(--mglon-schedule-primary-50);--mglon-schedule-error: #ea4335;--mglon-schedule-on-error: #ffffff;--mglon-schedule-spacing-xs: 4px;--mglon-schedule-spacing-sm: 8px;--mglon-schedule-spacing-md: 16px;--mglon-schedule-spacing-lg: 24px;--mglon-schedule-padding-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-padding-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-padding-md: var(--mglon-schedule-spacing-md);--mglon-schedule-padding-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-margin-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-margin-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-margin-md: var(--mglon-schedule-spacing-md);--mglon-schedule-margin-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-gap-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-gap-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-gap-md: var(--mglon-schedule-spacing-md);--mglon-schedule-gap-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-border-width-sm: 1px;--mglon-schedule-border-width-md: 2px;--mglon-schedule-border-width-lg: 4px;--mglon-schedule-header-height: 48px;--mglon-schedule-sidebar-width: 60px;--mglon-schedule-cell-height: 48px;--mglon-schedule-radius-sm: 4px;--mglon-schedule-radius-md: 8px;--mglon-schedule-radius-lg: 16px;--mglon-schedule-radius-full: 9999px;--mglon-schedule-font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;--mglon-schedule-font-size-xs: 10px;--mglon-schedule-font-size-sm: 12px;--mglon-schedule-font-size-md: 14px;--mglon-schedule-font-size-lg: 16px;--mglon-schedule-shadow-sm: 0 1px 2px rgba(0, 0, 0, .1);--mglon-schedule-shadow-md: 0 2px 4px rgba(0, 0, 0, .15);--mglon-schedule-shadow-lg: 0 4px 8px rgba(0, 0, 0, .2);--mglon-schedule-z-sticky: 10;--mglon-schedule-z-header: 20;--mglon-schedule-hover-bg: rgba(0, 0, 0, .04);--mglon-schedule-focus-ring-color: var(--mglon-schedule-primary-200);--mglon-schedule-disabled-opacity: .5;--mglon-schedule-transition-duration: .2s;--mglon-schedule-transition-easing: cubic-bezier(.4, 0, .2, 1);--mglon-schedule-month-week-display: grid;--mglon-schedule-month-week-columns: repeat(7, 1fr);--mglon-schedule-month-week-min-height: 80px;--mglon-schedule-month-week-border-bottom-width: 1px;display:block;--resource-view-bg: var(--mglon-resource-view-bg, var(--mglon-schedule-background));--resource-view-font-family: var(--mglon-resource-view-font-family, var(--mglon-schedule-font-family));--resource-view-text-color: var(--mglon-resource-view-text-color, var(--mglon-schedule-text-primary));--resource-sidebar-width: var(--mglon-resource-sidebar-width, var(--mglon-schedule-sidebar-width));--resource-header-height: var(--mglon-resource-header-height, var(--mglon-schedule-header-height));--resource-cell-height: var(--mglon-resource-cell-height, var(--mglon-schedule-cell-height));--resource-border: var(--mglon-resource-border, 1px solid var(--mglon-schedule-border));font-family:var(--resource-view-font-family);color:var(--resource-view-text-color);background-color:var(--resource-view-bg);height:100%;overflow:auto;position:relative}.ngx-cal-resource-view{display:grid;grid-template-columns:var(--resource-sidebar-width) 1fr;grid-template-rows:var(--resource-header-height) 1fr;min-width:fit-content}.ngx-cal-resource-view__header{display:contents}.ngx-cal-resource-view__corner{top:0;z-index:var(--mglon-schedule-z-header);position:sticky;left:0;z-index:var(--mglon-schedule-z-sticky);z-index:30;grid-row:1;grid-column:1;border-right:var(--resource-border);border-bottom:var(--resource-border);background-color:var(--mglon-schedule-surface)}.ngx-cal-resource-view__time-track{position:sticky;top:0;z-index:var(--mglon-schedule-z-header);grid-row:1;grid-column:2;display:flex;border-bottom:var(--resource-border);background-color:var(--mglon-schedule-surface)}.ngx-cal-resource-view__time-cell{display:flex;align-items:center;justify-content:center;flex:0 0 100px;height:100%;border-right:var(--resource-border);font-size:var(--mglon-schedule-font-size-sm);color:var(--mglon-schedule-text-secondary)}.ngx-cal-resource-view__sidebar{grid-row:2;grid-column:1;display:block;background-color:var(--mglon-schedule-surface)}.ngx-cal-resource-view__resource-cell{position:sticky;left:0;z-index:var(--mglon-schedule-z-sticky);height:var(--resource-cell-height);display:flex;align-items:center;padding:0 var(--mglon-schedule-spacing-sm);border-right:var(--resource-border);border-bottom:var(--resource-border);font-size:var(--mglon-schedule-font-size-md);background-color:var(--mglon-schedule-surface);width:var(--resource-sidebar-width);box-sizing:border-box;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ngx-cal-resource-view__grid{grid-row:2;grid-column:2;display:block}.ngx-cal-resource-view__row{display:flex;height:var(--resource-cell-height);border-bottom:var(--resource-border)}.ngx-cal-resource-view__cell{flex:0 0 100px;border-right:var(--resource-border);height:100%}.ngx-cal-resource-view__cell:hover{background-color:var(--mglon-schedule-hover)}\n"] }]
20
+ }] });
21
+
22
+ /**
23
+ * Type guard to check if an event is a regular Event
24
+ */
25
+ function isEvent(event) {
26
+ return event.type === 'event';
27
+ }
28
+ /**
29
+ * Type guard to check if an event is an AllDayEvent
30
+ */
31
+ function isAllDayEvent(event) {
32
+ return event.type === 'all-day';
33
+ }
34
+ /**
35
+ * Type guard to check if an event is a RecurrentEvent
36
+ */
37
+ function isRecurrentEvent(event) {
38
+ return event.type === 'recurrent';
39
+ }
40
+
41
+ /**
42
+ * Generates a complete month calendar grid with padding days from adjacent months.
43
+ * Always starts on Sunday and includes complete weeks (5-6 weeks total).
44
+ *
45
+ * @example
46
+ * getMonthCalendarGrid(new Date(2024, 0, 15)) // January 2024
47
+ * // Returns 5 weeks: Dec 31, 2023 → Feb 3, 2024
48
+ */
49
+ function getMonthCalendarGrid(date) {
50
+ const monthStart = startOfMonth(date);
51
+ const monthEnd = endOfMonth(date);
52
+ const calendarStart = startOfWeek(monthStart, { weekStartsOn: 0 });
53
+ const calendarEnd = endOfWeek(monthEnd, { weekStartsOn: 0 });
54
+ const allDays = eachDayOfInterval({ start: calendarStart, end: calendarEnd });
55
+ const calendarDays = allDays.map(day => ({
56
+ date: day,
57
+ day: day.getDate(),
58
+ isCurrentMonth: isSameMonth(day, date),
59
+ isPreviousMonth: day < monthStart,
60
+ isNextMonth: day > monthEnd,
61
+ }));
62
+ const weeks = [];
63
+ for (let i = 0; i < calendarDays.length; i += 7) {
64
+ weeks.push({ days: calendarDays.slice(i, i + 7) });
65
+ }
66
+ return weeks;
67
+ }
68
+ // ============================================================================
69
+ // WEEK VIEW - Interfaces & Functions
70
+ // ============================================================================
71
+ const DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
72
+ /**
73
+ * Generates the 7 days of the week containing the given date.
74
+ * Week starts on Sunday.
75
+ *
76
+ * @example
77
+ * getWeekDays(new Date(2024, 0, 15)) // Monday, Jan 15
78
+ * // Returns: Sun Jan 14 → Sat Jan 20
79
+ */
80
+ function getWeekDays(date) {
81
+ const weekStart = startOfWeek(date, { weekStartsOn: 0 });
82
+ const weekEnd = endOfWeek(date, { weekStartsOn: 0 });
83
+ const days = eachDayOfInterval({ start: weekStart, end: weekEnd });
84
+ const today = new Date();
85
+ today.setHours(0, 0, 0, 0);
86
+ return days.map(day => {
87
+ const dayDate = new Date(day);
88
+ dayDate.setHours(0, 0, 0, 0);
89
+ return {
90
+ date: dayDate,
91
+ dayOfWeek: day.getDay(),
92
+ dayName: DAY_NAMES[day.getDay()],
93
+ dayNumber: day.getDate(),
94
+ isToday: dayDate.getTime() === today.getTime()
95
+ };
96
+ });
97
+ }
98
+ /**
99
+ * Generates a 2D matrix of time slots for a week.
100
+ * Each day contains 48 slots (24h × 2 = 30-minute intervals).
101
+ *
102
+ * @returns TimeSlot[7 days][48 slots per day]
103
+ */
104
+ function getWeekTimeSlots(date) {
105
+ const weekDays = getWeekDays(date);
106
+ const today = new Date();
107
+ today.setHours(0, 0, 0, 0);
108
+ return weekDays.map(weekDay => {
109
+ const slots = [];
110
+ for (let hour = 0; hour < 24; hour++) {
111
+ for (let minute = 0; minute < 60; minute += 30) {
112
+ const slotDate = new Date(weekDay.date);
113
+ slotDate.setHours(hour, minute, 0, 0);
114
+ slots.push({
115
+ date: slotDate,
116
+ hour,
117
+ minute,
118
+ dayOfWeek: weekDay.dayOfWeek,
119
+ isToday: weekDay.isToday,
120
+ timeLabel: `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
121
+ });
122
+ }
123
+ }
124
+ return slots;
125
+ });
126
+ }
127
+ // ============================================================================
128
+ // VIEW RANGE - Functions for calculating visible date ranges
129
+ // ============================================================================
130
+ /**
131
+ * Calculates the visible date range based on view mode.
132
+ * Used to filter which events should be displayed.
133
+ */
134
+ function getViewRange(date, viewMode) {
135
+ const normalizedDate = new Date(date);
136
+ normalizedDate.setHours(0, 0, 0, 0);
137
+ switch (viewMode) {
138
+ case 'month': {
139
+ const monthStart = startOfMonth(normalizedDate);
140
+ const monthEnd = endOfMonth(normalizedDate);
141
+ return {
142
+ start: startOfWeek(monthStart, { weekStartsOn: 0 }),
143
+ end: endOfWeek(monthEnd, { weekStartsOn: 0 })
144
+ };
145
+ }
146
+ case 'week': {
147
+ return {
148
+ start: startOfWeek(normalizedDate, { weekStartsOn: 0 }),
149
+ end: endOfWeek(normalizedDate, { weekStartsOn: 0 })
150
+ };
151
+ }
152
+ case 'day':
153
+ case 'resource': {
154
+ const endOfDay = new Date(normalizedDate);
155
+ endOfDay.setHours(23, 59, 59, 999);
156
+ return { start: normalizedDate, end: endOfDay };
157
+ }
158
+ case 'list': {
159
+ return {
160
+ start: startOfMonth(normalizedDate),
161
+ end: endOfMonth(normalizedDate)
162
+ };
163
+ }
164
+ default:
165
+ return { start: normalizedDate, end: normalizedDate };
166
+ }
167
+ }
168
+ // ============================================================================
169
+ // EVENT FILTERING - Functions to check event visibility
170
+ // ============================================================================
171
+ /**
172
+ * Checks if an event overlaps with a given date range.
173
+ * Handles standard events, all-day events, and recurrent events.
174
+ */
175
+ function isEventInRange(event, range) {
176
+ if (isEvent(event) || isRecurrentEvent(event)) {
177
+ return event.start <= range.end && event.end >= range.start;
178
+ }
179
+ if (isAllDayEvent(event)) {
180
+ const eventStart = startOfDay(event.date);
181
+ const eventEnd = event.endDate ? endOfDay(event.endDate) : endOfDay(event.date);
182
+ return eventStart <= range.end && eventEnd >= range.start;
183
+ }
184
+ return false;
185
+ }
186
+
187
+ /**
188
+ * Color manipulation utilities for dynamic theming
189
+ */
190
+ /**
191
+ * Converts hex color to RGB
192
+ */
193
+ function hexToRgb(hex) {
194
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
195
+ return result ? {
196
+ r: parseInt(result[1], 16),
197
+ g: parseInt(result[2], 16),
198
+ b: parseInt(result[3], 16)
199
+ } : null;
200
+ }
201
+ /**
202
+ * Converts RGB to hex
203
+ */
204
+ function rgbToHex(r, g, b) {
205
+ return "#" + [r, g, b].map(x => {
206
+ const hex = Math.round(x).toString(16);
207
+ return hex.length === 1 ? "0" + hex : hex;
208
+ }).join('');
209
+ }
210
+ /**
211
+ * Converts RGB to HSL
212
+ */
213
+ function rgbToHsl(r, g, b) {
214
+ r /= 255;
215
+ g /= 255;
216
+ b /= 255;
217
+ const max = Math.max(r, g, b);
218
+ const min = Math.min(r, g, b);
219
+ let h = 0;
220
+ let s = 0;
221
+ const l = (max + min) / 2;
222
+ if (max !== min) {
223
+ const d = max - min;
224
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
225
+ switch (max) {
226
+ case r:
227
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
228
+ break;
229
+ case g:
230
+ h = ((b - r) / d + 2) / 6;
231
+ break;
232
+ case b:
233
+ h = ((r - g) / d + 4) / 6;
234
+ break;
235
+ }
236
+ }
237
+ return { h: h * 360, s: s * 100, l: l * 100 };
238
+ }
239
+ /**
240
+ * Converts HSL to RGB
241
+ */
242
+ function hslToRgb(h, s, l) {
243
+ h /= 360;
244
+ s /= 100;
245
+ l /= 100;
246
+ let r, g, b;
247
+ if (s === 0) {
248
+ r = g = b = l;
249
+ }
250
+ else {
251
+ const hue2rgb = (p, q, t) => {
252
+ if (t < 0)
253
+ t += 1;
254
+ if (t > 1)
255
+ t -= 1;
256
+ if (t < 1 / 6)
257
+ return p + (q - p) * 6 * t;
258
+ if (t < 1 / 2)
259
+ return q;
260
+ if (t < 2 / 3)
261
+ return p + (q - p) * (2 / 3 - t) * 6;
262
+ return p;
263
+ };
264
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
265
+ const p = 2 * l - q;
266
+ r = hue2rgb(p, q, h + 1 / 3);
267
+ g = hue2rgb(p, q, h);
268
+ b = hue2rgb(p, q, h - 1 / 3);
269
+ }
270
+ return { r: r * 255, g: g * 255, b: b * 255 };
271
+ }
272
+ /**
273
+ * Calculate relative luminance (WCAG formula)
274
+ */
275
+ function getLuminance(r, g, b) {
276
+ const [rs, gs, bs] = [r, g, b].map(c => {
277
+ c = c / 255;
278
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
279
+ });
280
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
281
+ }
282
+ /**
283
+ * Calculate contrast ratio between two colors
284
+ */
285
+ function getContrastRatio(rgb1, rgb2) {
286
+ const lum1 = getLuminance(rgb1.r, rgb1.g, rgb1.b);
287
+ const lum2 = getLuminance(rgb2.r, rgb2.g, rgb2.b);
288
+ const lighter = Math.max(lum1, lum2);
289
+ const darker = Math.min(lum1, lum2);
290
+ return (lighter + 0.05) / (darker + 0.05);
291
+ }
292
+ /**
293
+ * Generates a hover color (darker variant)
294
+ * If the base color is already dark, it slightly lightens instead
295
+ */
296
+ function getHoverColor(baseColor) {
297
+ const rgb = hexToRgb(baseColor);
298
+ if (!rgb)
299
+ return baseColor;
300
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
301
+ // If color is very dark (L < 20%), lighten it
302
+ // Otherwise, darken it
303
+ if (hsl.l < 20) {
304
+ hsl.l = Math.min(100, hsl.l + 15);
305
+ hsl.s = Math.min(100, hsl.s + 10);
306
+ }
307
+ else {
308
+ hsl.l = Math.max(0, hsl.l - 12);
309
+ hsl.s = Math.min(100, hsl.s + 8);
310
+ }
311
+ const newRgb = hslToRgb(hsl.h, hsl.s, hsl.l);
312
+ return rgbToHex(newRgb.r, newRgb.g, newRgb.b);
313
+ }
314
+ /**
315
+ * Generates a very light background variant (almost white)
316
+ */
317
+ function getLightBackgroundColor(baseColor) {
318
+ const rgb = hexToRgb(baseColor);
319
+ if (!rgb)
320
+ return '#f5f5f5';
321
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
322
+ // Keep the hue, reduce saturation, increase lightness to 95-97%
323
+ hsl.s = Math.min(30, hsl.s * 0.3);
324
+ hsl.l = 96;
325
+ const newRgb = hslToRgb(hsl.h, hsl.s, hsl.l);
326
+ return rgbToHex(newRgb.r, newRgb.g, newRgb.b);
327
+ }
328
+ /**
329
+ * Generates optimal text color (white or dark) for contrast
330
+ * Returns white for dark backgrounds, dark for light backgrounds
331
+ */
332
+ function getTextColor(backgroundColor) {
333
+ const rgb = hexToRgb(backgroundColor);
334
+ if (!rgb)
335
+ return '#000000';
336
+ // Calculate luminance
337
+ const luminance = getLuminance(rgb.r, rgb.g, rgb.b);
338
+ // If background is light (luminance > 0.5), use dark text
339
+ // Otherwise use white text
340
+ if (luminance > 0.5) {
341
+ // For very light backgrounds, use a dark gray/color-tinted text
342
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
343
+ // Keep hue, high saturation, low lightness
344
+ const textHsl = { h: hsl.h, s: Math.min(60, hsl.s * 0.8), l: 25 };
345
+ const textRgb = hslToRgb(textHsl.h, textHsl.s, textHsl.l);
346
+ return rgbToHex(textRgb.r, textRgb.g, textRgb.b);
347
+ }
348
+ else {
349
+ return '#ffffff';
350
+ }
351
+ }
352
+ /**
353
+ * Generates a full adaptive color scheme based on a single input color.
354
+ * It detects the Nature of the input (Vivid, Pastel, Dark) and preserves it,
355
+ * but also provides a "raw" variant using the color exactly as provided.
356
+ */
357
+ function generateAdaptiveColorScheme(baseColor) {
358
+ const rgb = hexToRgb(baseColor);
359
+ if (!rgb) {
360
+ const fallback = '#1a73e8';
361
+ return generateAdaptiveColorScheme(fallback);
362
+ }
363
+ const { h, s, l } = rgbToHsl(rgb.r, rgb.g, rgb.b);
364
+ // 1. Determine the "Nature" of the input color
365
+ let type;
366
+ if (l > 80)
367
+ type = 'pastel';
368
+ else if (l < 20)
369
+ type = 'dark'; // Only extremely dark colors are "Dark"
370
+ else
371
+ type = 'vivid';
372
+ // 2. Generate variants, keeping the input for its detected category
373
+ const variants = {};
374
+ // Raw (Always use original input)
375
+ variants.raw = generateVariant(h, s, l);
376
+ // Vivid
377
+ if (type === 'vivid') {
378
+ variants.vivid = variants.raw;
379
+ }
380
+ else {
381
+ variants.vivid = generateVariant(h, Math.max(s, 70), 50); // Boost to vivid
382
+ }
383
+ // Pastel
384
+ if (type === 'pastel') {
385
+ variants.pastel = generateVariant(h, s, l); // Use original
386
+ }
387
+ else {
388
+ // Soften: high lightness, moderate saturation
389
+ variants.pastel = generateVariant(h, Math.min(s, 65), 92);
390
+ }
391
+ // Dark
392
+ if (type === 'dark') {
393
+ variants.dark = generateVariant(h, s, l); // Use original
394
+ }
395
+ else {
396
+ // Deepen: low lightness, moderate saturation
397
+ variants.dark = generateVariant(h, Math.min(s, 60), 25);
398
+ }
399
+ return variants;
400
+ }
401
+ /**
402
+ * Internal helper to generate a specific variant with its hover and text colors
403
+ */
404
+ function generateVariant(h, s, l) {
405
+ const base = hslToHex(h, s, l);
406
+ // Hover: slightly more saturated and darker (or lighter if base is very dark)
407
+ const hoverL = l > 20 ? Math.max(0, l - 8) : l + 15;
408
+ const hoverS = Math.min(100, s + 5);
409
+ const hover = hslToHex(h, hoverS, hoverL);
410
+ return {
411
+ base,
412
+ hover,
413
+ text: getTextColor(base),
414
+ textHover: getTextColor(hover)
415
+ };
416
+ }
417
+ /**
418
+ * Simple HSL to Hex bridge
419
+ */
420
+ function hslToHex(h, s, l) {
421
+ const rgb = hslToRgb(h, s, l);
422
+ return rgbToHex(rgb.r, rgb.g, rgb.b);
423
+ }
424
+ /**
425
+ * Resolves the effective color for an event
426
+ */
427
+ function getEventColor(event, getResource, defaultColor) {
428
+ if (event.color)
429
+ return event.color;
430
+ if (event.resourceId) {
431
+ const resource = getResource(event.resourceId);
432
+ if (resource?.color)
433
+ return resource.color;
434
+ }
435
+ return defaultColor;
436
+ }
437
+
438
+ /**
439
+ * Header component for the week view displaying the 7 days of the week.
440
+ *
441
+ * Shows day names and numbers in a sticky header that remains visible
442
+ * when scrolling through time slots.
443
+ *
444
+ * Features:
445
+ * - Sticky positioning
446
+ * - Highlights today
447
+ * - Shows day name (Sun, Mon, etc.) and day number
448
+ *
449
+ * @example
450
+ * ```html
451
+ * <mglon-week-header [currentDate]="selectedDate"></mglon-week-header>
452
+ * ```
453
+ */
454
+ class WeekHeader {
455
+ /**
456
+ * The date to display the week for.
457
+ * Can be any date within the target week.
458
+ */
459
+ currentDate = input(new Date(), ...(ngDevMode ? [{ debugName: "currentDate" }] : []));
460
+ /**
461
+ * Computed array of the 7 days in the week
462
+ */
463
+ weekDays = computed(() => {
464
+ return getWeekDays(this.currentDate());
465
+ }, ...(ngDevMode ? [{ debugName: "weekDays" }] : []));
466
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WeekHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
467
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: WeekHeader, isStandalone: true, selector: "mglon-week-header", inputs: { currentDate: { classPropertyName: "currentDate", publicName: "currentDate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"mglon-week-header\">\n <!-- Time column spacer -->\n <div class=\"mglon-week-header__time-column\"></div>\n\n <!-- Day columns -->\n @for (day of weekDays(); track day.date.getTime()) {\n <div class=\"mglon-week-header__day\" [class.mglon-week-header__day--today]=\"day.isToday\">\n <div class=\"mglon-week-header__day-name\">{{ day.dayName }}</div>\n <div class=\"mglon-week-header__day-number\">\n <span>{{ day.dayNumber }}</span>\n </div>\n </div>\n }\n</div>", styles: [".mglon-week-header{--week-header-time-col-width: var(--mglon-week-header-time-column-width, 60px);--week-header-bg: var(--mglon-week-header-bg, var(--mglon-schedule-surface));--week-header-border: var(--mglon-week-header-border, 2px solid var(--mglon-schedule-border-color));--week-header-scrollbar-width: var(--mglon-week-header-scrollbar-width, 8px);--week-header-z-index: var(--mglon-week-header-z-index, 10);--week-header-day-padding: var(--mglon-week-header-day-padding, 8px 4px);--week-header-day-name-size: var(--mglon-week-header-day-name-size, 11px);--week-header-day-name-color: var(--mglon-week-header-day-name-color, var(--mglon-schedule-on-surface-variant));--week-header-day-number-size: var(--mglon-week-header-day-number-size, 16px);--week-header-day-number-color: var(--mglon-week-header-day-number-color, var(--mglon-schedule-on-surface));--week-header-today-bg: var(--mglon-week-header-today-bg, var(--mglon-schedule-error));--week-header-today-color: var(--mglon-week-header-today-color, var(--mglon-schedule-on-error));--week-header-today-size: var(--mglon-week-header-today-size, 24px);display:grid;grid-template-columns:var(--week-header-time-col-width) repeat(7,1fr);position:sticky;top:0;z-index:var(--week-header-z-index);background:var(--week-header-bg);border-bottom:var(--week-header-border);padding-right:var(--week-header-scrollbar-width)}.mglon-week-header__time-column{border-right:1px solid var(--mglon-schedule-border-color)}.mglon-week-header__day{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--week-header-day-padding);border-right:1px solid var(--mglon-schedule-border-color)}.mglon-week-header__day--today{position:relative}.mglon-week-header__day--today .mglon-week-header__day-number{background:var(--week-header-today-bg);color:var(--week-header-today-color);width:var(--week-header-today-size);height:var(--week-header-today-size);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:500;position:relative;border-radius:50% 50% 50% 0;transform:rotate(-45deg)}.mglon-week-header__day--today .mglon-week-header__day-number span{display:block;transform:rotate(45deg)}.mglon-week-header__day-name{font-size:var(--week-header-day-name-size);font-weight:500;color:var(--week-header-day-name-color);text-transform:uppercase;margin-bottom:4px}.mglon-week-header__day-number{font-size:var(--week-header-day-number-size);font-weight:400;color:var(--week-header-day-number-color)}\n"] });
468
+ }
469
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WeekHeader, decorators: [{
470
+ type: Component,
471
+ args: [{ selector: 'mglon-week-header', standalone: true, imports: [], template: "<div class=\"mglon-week-header\">\n <!-- Time column spacer -->\n <div class=\"mglon-week-header__time-column\"></div>\n\n <!-- Day columns -->\n @for (day of weekDays(); track day.date.getTime()) {\n <div class=\"mglon-week-header__day\" [class.mglon-week-header__day--today]=\"day.isToday\">\n <div class=\"mglon-week-header__day-name\">{{ day.dayName }}</div>\n <div class=\"mglon-week-header__day-number\">\n <span>{{ day.dayNumber }}</span>\n </div>\n </div>\n }\n</div>", styles: [".mglon-week-header{--week-header-time-col-width: var(--mglon-week-header-time-column-width, 60px);--week-header-bg: var(--mglon-week-header-bg, var(--mglon-schedule-surface));--week-header-border: var(--mglon-week-header-border, 2px solid var(--mglon-schedule-border-color));--week-header-scrollbar-width: var(--mglon-week-header-scrollbar-width, 8px);--week-header-z-index: var(--mglon-week-header-z-index, 10);--week-header-day-padding: var(--mglon-week-header-day-padding, 8px 4px);--week-header-day-name-size: var(--mglon-week-header-day-name-size, 11px);--week-header-day-name-color: var(--mglon-week-header-day-name-color, var(--mglon-schedule-on-surface-variant));--week-header-day-number-size: var(--mglon-week-header-day-number-size, 16px);--week-header-day-number-color: var(--mglon-week-header-day-number-color, var(--mglon-schedule-on-surface));--week-header-today-bg: var(--mglon-week-header-today-bg, var(--mglon-schedule-error));--week-header-today-color: var(--mglon-week-header-today-color, var(--mglon-schedule-on-error));--week-header-today-size: var(--mglon-week-header-today-size, 24px);display:grid;grid-template-columns:var(--week-header-time-col-width) repeat(7,1fr);position:sticky;top:0;z-index:var(--week-header-z-index);background:var(--week-header-bg);border-bottom:var(--week-header-border);padding-right:var(--week-header-scrollbar-width)}.mglon-week-header__time-column{border-right:1px solid var(--mglon-schedule-border-color)}.mglon-week-header__day{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--week-header-day-padding);border-right:1px solid var(--mglon-schedule-border-color)}.mglon-week-header__day--today{position:relative}.mglon-week-header__day--today .mglon-week-header__day-number{background:var(--week-header-today-bg);color:var(--week-header-today-color);width:var(--week-header-today-size);height:var(--week-header-today-size);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:500;position:relative;border-radius:50% 50% 50% 0;transform:rotate(-45deg)}.mglon-week-header__day--today .mglon-week-header__day-number span{display:block;transform:rotate(45deg)}.mglon-week-header__day-name{font-size:var(--week-header-day-name-size);font-weight:500;color:var(--week-header-day-name-color);text-transform:uppercase;margin-bottom:4px}.mglon-week-header__day-number{font-size:var(--week-header-day-number-size);font-weight:400;color:var(--week-header-day-number-color)}\n"] }]
472
+ }], propDecorators: { currentDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentDate", required: false }] }] } });
473
+
474
+ /**
475
+ * Component representing a single 30-minute time slot in the week view.
476
+ *
477
+ * Displays the time label (e.g., "09:00") only for the first column (time gutter).
478
+ * Each slot has a minimum height of 50px and can contain events.
479
+ *
480
+ * @example
481
+ * ```html
482
+ * <mglon-time-slot
483
+ * [slot]="timeSlot"
484
+ * [showTimeLabel]="true">
485
+ * </mglon-time-slot>
486
+ * ```
487
+ */
488
+ class TimeSlotComponent {
489
+ /**
490
+ * The time slot data to display
491
+ */
492
+ slot = input.required(...(ngDevMode ? [{ debugName: "slot" }] : []));
493
+ /**
494
+ * Whether to show the time label (only true for first column - time gutter)
495
+ */
496
+ showTimeLabel = input(false, ...(ngDevMode ? [{ debugName: "showTimeLabel" }] : []));
497
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TimeSlotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
498
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TimeSlotComponent, isStandalone: true, selector: "mglon-time-slot", inputs: { slot: { classPropertyName: "slot", publicName: "slot", isSignal: true, isRequired: true, transformFunction: null }, showTimeLabel: { classPropertyName: "showTimeLabel", publicName: "showTimeLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"mglon-time-slot\" [class.mglon-time-slot--hour]=\"slot().minute === 0\"\n [class.mglon-time-slot--today]=\"slot().isToday\">\n @if (showTimeLabel()) {\n <span class=\"mglon-time-slot__time-label\">{{ slot().timeLabel }}</span>\n }\n</div>", styles: [".mglon-time-slot{--time-slot-height: var(--mglon-time-slot-height, 50px);--time-slot-border-color: var(--mglon-time-slot-border-color, var(--mglon-schedule-border-color));--time-slot-border-col-light: var(--mglon-time-slot-light-border-color, var(--mglon-schedule-border-color-light, #f0f0f0));--time-slot-bg: var(--mglon-time-slot-bg, var(--mglon-schedule-surface));--time-slot-label-color: var(--mglon-time-slot-label-color, var(--mglon-schedule-on-surface-variant));--time-slot-label-size: var(--mglon-time-slot-label-size, 12px);min-height:var(--time-slot-height);border-right:1px solid var(--time-slot-border-color);border-bottom:1px solid var(--time-slot-border-col-light);position:relative;background:var(--time-slot-bg)}.mglon-time-slot__time-label{position:absolute;top:-8px;right:8px;font-size:var(--time-slot-label-size);color:var(--time-slot-label-color);background:var(--time-slot-bg);padding:0 4px}\n"] });
499
+ }
500
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TimeSlotComponent, decorators: [{
501
+ type: Component,
502
+ args: [{ selector: 'mglon-time-slot', standalone: true, imports: [], template: "<div class=\"mglon-time-slot\" [class.mglon-time-slot--hour]=\"slot().minute === 0\"\n [class.mglon-time-slot--today]=\"slot().isToday\">\n @if (showTimeLabel()) {\n <span class=\"mglon-time-slot__time-label\">{{ slot().timeLabel }}</span>\n }\n</div>", styles: [".mglon-time-slot{--time-slot-height: var(--mglon-time-slot-height, 50px);--time-slot-border-color: var(--mglon-time-slot-border-color, var(--mglon-schedule-border-color));--time-slot-border-col-light: var(--mglon-time-slot-light-border-color, var(--mglon-schedule-border-color-light, #f0f0f0));--time-slot-bg: var(--mglon-time-slot-bg, var(--mglon-schedule-surface));--time-slot-label-color: var(--mglon-time-slot-label-color, var(--mglon-schedule-on-surface-variant));--time-slot-label-size: var(--mglon-time-slot-label-size, 12px);min-height:var(--time-slot-height);border-right:1px solid var(--time-slot-border-color);border-bottom:1px solid var(--time-slot-border-col-light);position:relative;background:var(--time-slot-bg)}.mglon-time-slot__time-label{position:absolute;top:-8px;right:8px;font-size:var(--time-slot-label-size);color:var(--time-slot-label-color);background:var(--time-slot-bg);padding:0 4px}\n"] }]
503
+ }], propDecorators: { slot: [{ type: i0.Input, args: [{ isSignal: true, alias: "slot", required: true }] }], showTimeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTimeLabel", required: false }] }] } });
504
+
505
+ class Selection {
506
+ // Input for the selection corners (null means no selection)
507
+ selection = input(null, ...(ngDevMode ? [{ debugName: "selection" }] : []));
508
+ // Input to control visibility (only show while actively selecting)
509
+ isSelecting = input(false, ...(ngDevMode ? [{ debugName: "isSelecting" }] : []));
510
+ // Computed style for the indicator based on selection corners
511
+ indicatorStyle = computed(() => {
512
+ const sel = this.selection();
513
+ const selecting = this.isSelecting();
514
+ if (!sel || !selecting) {
515
+ return { display: 'none' };
516
+ }
517
+ return {
518
+ top: `${sel.top}px`,
519
+ left: `${sel.left}px`,
520
+ width: `${sel.right - sel.left}px`,
521
+ height: `${sel.bottom - sel.top}px`,
522
+ display: 'block'
523
+ };
524
+ }, ...(ngDevMode ? [{ debugName: "indicatorStyle" }] : []));
525
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Selection, deps: [], target: i0.ɵɵFactoryTarget.Component });
526
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: Selection, isStandalone: true, selector: "mglon-selection", inputs: { selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, isSelecting: { classPropertyName: "isSelecting", publicName: "isSelecting", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"mglon-selection__container\">\n <span class=\"mglon-selection__indicator\" [ngStyle]=\"indicatorStyle()\"></span>\n</div>", styles: [":host{display:block;position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mglon-selection__container{position:relative;width:100%;height:100%}.mglon-selection__indicator{position:absolute;pointer-events:none;background-color:var(--mglon-selection-background, color-mix(in srgb, var(--mglon-schedule-primary-900, #1a237e) 30%, transparent));background-image:repeating-linear-gradient(to right,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 0,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 12px,transparent 12px,transparent 20px),repeating-linear-gradient(to right,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 0,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 12px,transparent 12px,transparent 20px),repeating-linear-gradient(to bottom,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 0,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 12px,transparent 12px,transparent 20px),repeating-linear-gradient(to bottom,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 0,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 12px,transparent 12px,transparent 20px);background-size:100% var(--mglon-selection-border-width, 2px),100% var(--mglon-selection-border-width, 2px),var(--mglon-selection-border-width, 2px) 100%,var(--mglon-selection-border-width, 2px) 100%;background-position:0 0,0 100%,0 0,100% 0;background-repeat:no-repeat}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
527
+ }
528
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Selection, decorators: [{
529
+ type: Component,
530
+ args: [{ selector: 'mglon-selection', standalone: true, imports: [CommonModule], template: "<div class=\"mglon-selection__container\">\n <span class=\"mglon-selection__indicator\" [ngStyle]=\"indicatorStyle()\"></span>\n</div>", styles: [":host{display:block;position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mglon-selection__container{position:relative;width:100%;height:100%}.mglon-selection__indicator{position:absolute;pointer-events:none;background-color:var(--mglon-selection-background, color-mix(in srgb, var(--mglon-schedule-primary-900, #1a237e) 30%, transparent));background-image:repeating-linear-gradient(to right,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 0,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 12px,transparent 12px,transparent 20px),repeating-linear-gradient(to right,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 0,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 12px,transparent 12px,transparent 20px),repeating-linear-gradient(to bottom,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 0,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 12px,transparent 12px,transparent 20px),repeating-linear-gradient(to bottom,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 0,var(--mglon-selection-border-color, var(--mglon-schedule-primary)) 12px,transparent 12px,transparent 20px);background-size:100% var(--mglon-selection-border-width, 2px),100% var(--mglon-selection-border-width, 2px),var(--mglon-selection-border-width, 2px) 100%,var(--mglon-selection-border-width, 2px) 100%;background-position:0 0,0 100%,0 0,100% 0;background-repeat:no-repeat}\n"] }]
531
+ }], propDecorators: { selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], isSelecting: [{ type: i0.Input, args: [{ isSignal: true, alias: "isSelecting", required: false }] }] } });
532
+
533
+ const CELL_HEADER_HEIGHT = 24;
534
+ /** Height of each event slot row in pixels */
535
+ const SLOT_HEIGHT = 20;
536
+ /** Vertical gap between event slots in pixels */
537
+ const SLOT_GAP = 2;
538
+ /** Default number of visible event rows in month view */
539
+ const DEFAULT_VISIBLE_EVENT_ROWS = 3;
540
+ const DEFAULT_CONFIG = {
541
+ initialDate: new Date(),
542
+ initialView: 'month',
543
+ views: ['month', 'week', 'day', 'resource'],
544
+ slotDuration: 30,
545
+ dayStartHour: 0,
546
+ dayEndHour: 23,
547
+ resourceSidebarWidth: 200,
548
+ allowOverlaps: true,
549
+ locale: 'en',
550
+ weekStartsOn: 0,
551
+ theme: 'theme-light', // Asumimos un tema claro por defecto
552
+ height: '100%',
553
+ visibleEventRows: DEFAULT_VISIBLE_EVENT_ROWS,
554
+ backgroundSelection: true,
555
+ showNowIndicator: true,
556
+ editable: true,
557
+ resizableEvents: true,
558
+ showSidebar: true
559
+ };
560
+ const DEFAULT_RESOURCE_INPUTS = {
561
+ tags: [],
562
+ isReadOnly: false,
563
+ isBlocked: false,
564
+ isActive: true,
565
+ resizableEvents: true
566
+ };
567
+ const DEFAULT_EVENT_INPUTS = {
568
+ color: '#3788d8',
569
+ allDay: false,
570
+ description: '',
571
+ data: null,
572
+ };
573
+
574
+ /**
575
+ * UI Configuration Types
576
+ *
577
+ * Type definitions organized by functional areas (Header, Sidebar, Grid)
578
+ * for controlling the visual appearance of schedule components.
579
+ */
580
+ /**
581
+ * Default UI configuration
582
+ */
583
+ const DEFAULT_UI_CONFIG = {
584
+ header: {
585
+ buttonGroup: {
586
+ appearance: 'solid',
587
+ rounded: 'md',
588
+ density: 'comfortable'
589
+ },
590
+ iconButtons: {
591
+ rounded: 'md'
592
+ },
593
+ todayButton: {
594
+ rounded: 'md',
595
+ appearance: 'ghost'
596
+ }
597
+ },
598
+ sidebar: {
599
+ resourceItems: {
600
+ rounded: 'sm',
601
+ density: 'comfortable'
602
+ }
603
+ },
604
+ grid: {
605
+ eventSlots: {
606
+ rounded: 'sm'
607
+ },
608
+ overflowIndicator: {
609
+ rounded: 'sm',
610
+ appearance: 'ghost'
611
+ },
612
+ useDynamicColors: true
613
+ }
614
+ };
615
+
616
+ const initialCalendarState = {
617
+ currentDate: new Date(),
618
+ viewMode: 'month',
619
+ config: DEFAULT_CONFIG,
620
+ uiConfig: DEFAULT_UI_CONFIG,
621
+ events: new Map(),
622
+ resources: new Map(),
623
+ weekRowHeight: 0,
624
+ expandedWeekIndex: null,
625
+ dragState: {
626
+ eventId: null,
627
+ grabDate: null,
628
+ hoverDate: null
629
+ },
630
+ resizeState: {
631
+ eventId: null,
632
+ side: null,
633
+ hoverDate: null
634
+ },
635
+ interactionMode: 'none',
636
+ hoveredEventId: null
637
+ };
638
+ const CalendarStore = signalStore({ providedIn: 'root' }, withState(initialCalendarState), withComputed(({ resources, events, currentDate, viewMode, config }) => ({
639
+ // Date & View Computeds
640
+ viewDate: computed(() => currentDate()),
641
+ currentView: computed(() => viewMode()),
642
+ viewRange: computed(() => getViewRange(currentDate(), viewMode())),
643
+ formattedDate: computed(() => {
644
+ const date = currentDate();
645
+ return new Intl.DateTimeFormat(config().locale, {
646
+ month: 'long',
647
+ year: 'numeric',
648
+ day: viewMode() === 'day' ? 'numeric' : undefined
649
+ }).format(date);
650
+ }),
651
+ // Resource Computeds
652
+ allResources: computed(() => Array.from(resources().values())),
653
+ resourceCount: computed(() => resources().size),
654
+ activeResources: computed(() => Array.from(resources().values()).filter(r => r.isActive !== false)),
655
+ inactiveResources: computed(() => Array.from(resources().values()).filter(r => r.isActive === false)),
656
+ // Event Computeds
657
+ allEvents: computed(() => Array.from(events().values())),
658
+ eventCount: computed(() => events().size),
659
+ currentViewEvents: computed(() => {
660
+ const range = getViewRange(currentDate(), viewMode());
661
+ const resMap = resources();
662
+ return Array.from(events().values()).filter(event => {
663
+ // Exclude 'recurrent' templates from rendering.
664
+ // Their expanded instances ('event' type) will be included instead.
665
+ if (event.type === 'recurrent')
666
+ return false;
667
+ const inRange = isEventInRange(event, range);
668
+ if (!inRange)
669
+ return false;
670
+ if (event.resourceId) {
671
+ const res = resMap.get(event.resourceId);
672
+ return res?.isActive !== false;
673
+ }
674
+ return true;
675
+ });
676
+ }),
677
+ /**
678
+ * Minimum height for a week row in month view.
679
+ * Calculated from: CELL_HEADER_HEIGHT + (n * SLOT_HEIGHT) + ((n-1) * SLOT_GAP)
680
+ * where n = visibleEventRows from config.
681
+ */
682
+ minWeekRowHeight: computed(() => {
683
+ const rows = config().visibleEventRows ?? DEFAULT_VISIBLE_EVENT_ROWS;
684
+ // Formula: header + (rows * slot height) + (rows * slot gap)
685
+ // The last gap serves as bottom padding for the row.
686
+ return CELL_HEADER_HEIGHT + (rows * SLOT_HEIGHT) + (rows * SLOT_GAP);
687
+ })
688
+ })), withMethods((store) => {
689
+ const interaction$ = new Subject();
690
+ return {
691
+ /** Returns the observable stream of all event interactions */
692
+ getInteractions() {
693
+ return interaction$.asObservable();
694
+ },
695
+ /** Dispatches a new interaction event to all subscribers */
696
+ dispatchInteraction(type, eventId, payload) {
697
+ interaction$.next({ type, eventId, payload });
698
+ },
699
+ // --- Navigation & View Methods ---
700
+ setDate(date) {
701
+ patchState(store, { currentDate: date });
702
+ },
703
+ changeView(view) {
704
+ patchState(store, { viewMode: view });
705
+ },
706
+ updateConfig(config) {
707
+ patchState(store, (state) => ({
708
+ config: { ...state.config, ...config },
709
+ currentDate: config.initialDate ? new Date(config.initialDate) : state.currentDate,
710
+ viewMode: config.initialView || state.viewMode
711
+ }));
712
+ if (config.resources) {
713
+ this.registerResources(config.resources);
714
+ }
715
+ },
716
+ next() {
717
+ const mode = store.viewMode();
718
+ const current = new Date(store.currentDate());
719
+ switch (mode) {
720
+ case 'day':
721
+ case 'resource':
722
+ current.setDate(current.getDate() + 1);
723
+ break;
724
+ case 'week':
725
+ current.setDate(current.getDate() + 7);
726
+ break;
727
+ case 'month':
728
+ current.setMonth(current.getMonth() + 1);
729
+ break;
730
+ }
731
+ patchState(store, { currentDate: current });
732
+ },
733
+ prev() {
734
+ const mode = store.viewMode();
735
+ const current = new Date(store.currentDate());
736
+ switch (mode) {
737
+ case 'day':
738
+ case 'resource':
739
+ current.setDate(current.getDate() - 1);
740
+ break;
741
+ case 'week':
742
+ current.setDate(current.getDate() - 7);
743
+ break;
744
+ case 'month':
745
+ current.setMonth(current.getMonth() - 1);
746
+ break;
747
+ }
748
+ patchState(store, { currentDate: current });
749
+ },
750
+ today() {
751
+ patchState(store, { currentDate: new Date() });
752
+ },
753
+ // --- Resource Methods ---
754
+ registerResources(resources) {
755
+ patchState(store, (state) => {
756
+ const newMap = new Map(state.resources);
757
+ resources.forEach(r => newMap.set(r.id, r));
758
+ return { resources: newMap };
759
+ });
760
+ },
761
+ registerResource(resource) {
762
+ patchState(store, (state) => {
763
+ const newMap = new Map(state.resources);
764
+ newMap.set(resource.id, resource);
765
+ return { resources: newMap };
766
+ });
767
+ },
768
+ updateResource(id, partial) {
769
+ patchState(store, (state) => {
770
+ const resource = state.resources.get(id);
771
+ if (!resource)
772
+ return state;
773
+ const newMap = new Map(state.resources);
774
+ newMap.set(id, { ...resource, ...partial });
775
+ return { resources: newMap };
776
+ });
777
+ },
778
+ unregisterResource(id) {
779
+ patchState(store, (state) => {
780
+ const newMap = new Map(state.resources);
781
+ newMap.delete(id);
782
+ return { resources: newMap };
783
+ });
784
+ },
785
+ getResource(id) {
786
+ return store.resources().get(id);
787
+ },
788
+ toggleResource(id) {
789
+ const resource = store.resources().get(id);
790
+ if (resource) {
791
+ this.updateResource(id, { isActive: resource.isActive === false });
792
+ }
793
+ },
794
+ showResource(id) {
795
+ this.updateResource(id, { isActive: true });
796
+ },
797
+ hideResource(id) {
798
+ this.updateResource(id, { isActive: false });
799
+ },
800
+ // --- Event Methods ---
801
+ setEvents(events) {
802
+ const eventsMap = new Map();
803
+ events.forEach(e => eventsMap.set(e.id, e));
804
+ patchState(store, { events: eventsMap });
805
+ },
806
+ registerEvent(event) {
807
+ patchState(store, (state) => {
808
+ const newMap = new Map(state.events);
809
+ newMap.set(event.id, event);
810
+ return { events: newMap };
811
+ });
812
+ },
813
+ updateEvent(id, partial) {
814
+ patchState(store, (state) => {
815
+ const event = state.events.get(id);
816
+ if (!event)
817
+ return state;
818
+ const newMap = new Map(state.events);
819
+ newMap.set(id, { ...event, ...partial });
820
+ return { events: newMap };
821
+ });
822
+ },
823
+ unregisterEvent(id) {
824
+ patchState(store, (state) => {
825
+ const newMap = new Map(state.events);
826
+ newMap.delete(id);
827
+ return { events: newMap };
828
+ });
829
+ },
830
+ getEvent(id) {
831
+ return store.events().get(id);
832
+ },
833
+ getEventsByResource(resourceId) {
834
+ return Array.from(store.events().values()).filter(event => event.resourceId === resourceId);
835
+ },
836
+ clear() {
837
+ patchState(store, {
838
+ events: new Map(),
839
+ resources: new Map(),
840
+ });
841
+ },
842
+ setUIConfig(config) {
843
+ patchState(store, (state) => ({
844
+ uiConfig: {
845
+ header: config.header
846
+ ? {
847
+ buttonGroup: config.header.buttonGroup
848
+ ? { ...state.uiConfig.header.buttonGroup, ...config.header.buttonGroup }
849
+ : state.uiConfig.header.buttonGroup,
850
+ iconButtons: config.header.iconButtons
851
+ ? { ...state.uiConfig.header.iconButtons, ...config.header.iconButtons }
852
+ : state.uiConfig.header.iconButtons,
853
+ todayButton: config.header.todayButton
854
+ ? { ...state.uiConfig.header.todayButton, ...config.header.todayButton }
855
+ : state.uiConfig.header.todayButton
856
+ }
857
+ : state.uiConfig.header,
858
+ sidebar: config.sidebar
859
+ ? {
860
+ resourceItems: config.sidebar.resourceItems
861
+ ? { ...state.uiConfig.sidebar.resourceItems, ...config.sidebar.resourceItems }
862
+ : state.uiConfig.sidebar.resourceItems,
863
+ background: config.sidebar.background !== undefined
864
+ ? config.sidebar.background
865
+ : state.uiConfig.sidebar.background,
866
+ rounded: config.sidebar.rounded !== undefined
867
+ ? config.sidebar.rounded
868
+ : state.uiConfig.sidebar.rounded
869
+ }
870
+ : state.uiConfig.sidebar,
871
+ grid: config.grid
872
+ ? {
873
+ eventSlots: config.grid.eventSlots
874
+ ? { ...state.uiConfig.grid.eventSlots, ...config.grid.eventSlots }
875
+ : state.uiConfig.grid.eventSlots,
876
+ overflowIndicator: config.grid.overflowIndicator
877
+ ? { ...state.uiConfig.grid.overflowIndicator, ...config.grid.overflowIndicator }
878
+ : state.uiConfig.grid.overflowIndicator,
879
+ useDynamicColors: config.grid.useDynamicColors !== undefined
880
+ ? config.grid.useDynamicColors
881
+ : state.uiConfig.grid.useDynamicColors
882
+ }
883
+ : state.uiConfig.grid
884
+ }
885
+ }));
886
+ },
887
+ /**
888
+ * Sets the height of a week row container.
889
+ * Called once from the first week's ResizeObserver.
890
+ */
891
+ setWeekRowHeight(height) {
892
+ patchState(store, { weekRowHeight: height });
893
+ },
894
+ /**
895
+ * Toggles expansion of a week. Only one week can be expanded at a time.
896
+ * If the same week is toggled twice, it collapses.
897
+ * @param weekIndex - The index of the week to toggle (0-5)
898
+ */
899
+ toggleWeekExpansion(weekIndex) {
900
+ const current = store.expandedWeekIndex();
901
+ patchState(store, {
902
+ expandedWeekIndex: current === weekIndex ? null : weekIndex
903
+ });
904
+ },
905
+ // --- Drag & Drop Methods ---
906
+ setDragStart(eventId, grabDate) {
907
+ patchState(store, {
908
+ dragState: {
909
+ eventId,
910
+ grabDate,
911
+ hoverDate: null
912
+ }
913
+ });
914
+ },
915
+ setDragHover(date) {
916
+ patchState(store, (state) => ({
917
+ dragState: {
918
+ ...state.dragState,
919
+ hoverDate: date
920
+ }
921
+ }));
922
+ },
923
+ clearDragState() {
924
+ patchState(store, {
925
+ dragState: {
926
+ eventId: null,
927
+ grabDate: null,
928
+ hoverDate: null
929
+ }
930
+ });
931
+ },
932
+ /**
933
+ * Updates the position of the currently dragged event based on the hover date.
934
+ * Calculates the offset between the original grab date and the new hover date.
935
+ */
936
+ updateDraggedEventPosition() {
937
+ const { eventId, grabDate, hoverDate } = store.dragState();
938
+ if (!eventId || !grabDate || !hoverDate)
939
+ return;
940
+ const event = store.events().get(eventId);
941
+ if (!event)
942
+ return;
943
+ const offset = differenceInCalendarDays(hoverDate, grabDate);
944
+ if (offset === 0)
945
+ return;
946
+ if (event.type === 'all-day') {
947
+ const partial = {
948
+ date: addDays(event.date, offset)
949
+ };
950
+ if (event.endDate) {
951
+ partial.endDate = addDays(event.endDate, offset);
952
+ }
953
+ this.updateEvent(eventId, partial);
954
+ }
955
+ else {
956
+ const partial = {
957
+ start: addDays(event.start, offset),
958
+ end: addDays(event.end, offset)
959
+ };
960
+ this.updateEvent(eventId, partial);
961
+ }
962
+ // Update the grab date to the new hover date to maintain the relative "grabbed" point
963
+ patchState(store, (state) => ({
964
+ dragState: {
965
+ ...state.dragState,
966
+ grabDate: hoverDate
967
+ }
968
+ }));
969
+ },
970
+ // --- Resizing Methods ---
971
+ setResizeStart(eventId, side) {
972
+ patchState(store, {
973
+ resizeState: {
974
+ eventId,
975
+ side,
976
+ hoverDate: null
977
+ }
978
+ });
979
+ },
980
+ setResizeHover(date) {
981
+ patchState(store, (state) => ({
982
+ resizeState: {
983
+ ...state.resizeState,
984
+ hoverDate: date
985
+ }
986
+ }));
987
+ },
988
+ clearResizeState() {
989
+ patchState(store, {
990
+ resizeState: {
991
+ eventId: null,
992
+ side: null,
993
+ hoverDate: null
994
+ }
995
+ });
996
+ },
997
+ /**
998
+ * Updates the start or end date of the currently resized event based on the hover date.
999
+ */
1000
+ updateResizedEvent() {
1001
+ const { eventId, side, hoverDate } = store.resizeState();
1002
+ if (!eventId || !side || !hoverDate)
1003
+ return;
1004
+ const event = store.events().get(eventId);
1005
+ if (!event)
1006
+ return;
1007
+ const normalizedHover = startOfDay(hoverDate);
1008
+ if (event.type === 'all-day') {
1009
+ const partial = {};
1010
+ const eventDate = startOfDay(event.date);
1011
+ const currentEnd = event.endDate ? startOfDay(event.endDate) : eventDate;
1012
+ if (side === 'left') {
1013
+ // Cannot exceed the current end date
1014
+ partial.date = normalizedHover > currentEnd ? currentEnd : normalizedHover;
1015
+ }
1016
+ else if (side === 'right') {
1017
+ // Cannot be before the current start date
1018
+ partial.endDate = normalizedHover < eventDate ? eventDate : normalizedHover;
1019
+ }
1020
+ this.updateEvent(eventId, partial);
1021
+ }
1022
+ else {
1023
+ const partial = {};
1024
+ if (side === 'left') {
1025
+ // Cannot exceed end time
1026
+ partial.start = hoverDate > event.end ? event.end : hoverDate;
1027
+ }
1028
+ else if (side === 'right') {
1029
+ // Cannot be before start time
1030
+ partial.end = hoverDate < event.start ? event.start : hoverDate;
1031
+ }
1032
+ this.updateEvent(eventId, partial);
1033
+ }
1034
+ },
1035
+ // --- Interaction Mutex ---
1036
+ setInteractionMode(mode) {
1037
+ patchState(store, { interactionMode: mode });
1038
+ },
1039
+ // --- Hover State Methods ---
1040
+ setHoveredEvent(eventId) {
1041
+ patchState(store, { hoveredEventId: eventId });
1042
+ }
1043
+ };
1044
+ }));
1045
+
1046
+ class Selectable {
1047
+ }
1048
+
1049
+ /**
1050
+ * Directive that enables mouse-based selection on calendar views.
1051
+ *
1052
+ * Works with components that implement the Selectable interface to translate
1053
+ * visual coordinates into dates/resources. Handles all mouse interaction logic
1054
+ * and emits selection events.
1055
+ *
1056
+ * @example
1057
+ * ```html
1058
+ * <div [mglonSelectable]="this"
1059
+ * (selectionStart)="onStart($event)"
1060
+ * (selectionChange)="onChange($event)"
1061
+ * (selectionEnd)="onEnd($event)">
1062
+ * </div>
1063
+ * ```
1064
+ */
1065
+ class SelectableDirective {
1066
+ elementRef = inject(ElementRef);
1067
+ platformId = inject(PLATFORM_ID);
1068
+ store = inject(CalendarStore);
1069
+ /**
1070
+ * The component that implements Selectable interface.
1071
+ * Provides the getDateFromPoint() method to translate coordinates to dates.
1072
+ */
1073
+ selectable = null;
1074
+ /**
1075
+ * Emitted when user starts a selection (mousedown)
1076
+ */
1077
+ selectionStart = output();
1078
+ /**
1079
+ * Emitted when selection changes (mousemove while selecting)
1080
+ */
1081
+ selectionChange = output();
1082
+ /**
1083
+ * Emitted when selection ends (mouseup)
1084
+ */
1085
+ selectionEnd = output();
1086
+ /**
1087
+ * Current selection rectangle coordinates (relative to container)
1088
+ */
1089
+ selection = signal(null, ...(ngDevMode ? [{ debugName: "selection" }] : []));
1090
+ /**
1091
+ * Whether user is currently selecting (mouse button is down)
1092
+ */
1093
+ isSelecting = signal(false, ...(ngDevMode ? [{ debugName: "isSelecting" }] : []));
1094
+ /** Starting point of the selection */
1095
+ startPoint = null;
1096
+ /** Date/resource data at the starting point */
1097
+ startData = null;
1098
+ /** Date/resource data at the current cursor position */
1099
+ currentData = null;
1100
+ /**
1101
+ * Handles mousedown event to initiate selection
1102
+ */
1103
+ onMouseDown(event) {
1104
+ if (!this.canSelect() || this.store.interactionMode() !== 'none')
1105
+ return;
1106
+ this.store.setInteractionMode('selecting');
1107
+ const coords = this.getRelativeCoordinates(event);
1108
+ this.startSelection(coords, event.clientX, event.clientY);
1109
+ }
1110
+ /**
1111
+ * Handles mousemove event to update selection
1112
+ */
1113
+ onMouseMove(event) {
1114
+ if (!this.isSelecting() || !this.startPoint || !this.canSelect())
1115
+ return;
1116
+ const coords = this.getRelativeCoordinates(event);
1117
+ this.updateSelection(coords, event.clientX, event.clientY);
1118
+ }
1119
+ /**
1120
+ * Handles mouseup event to complete selection
1121
+ */
1122
+ onMouseUp(event) {
1123
+ if (!this.isSelecting() || !this.canSelect())
1124
+ return;
1125
+ this.store.setInteractionMode('none');
1126
+ this.endSelection();
1127
+ }
1128
+ /**
1129
+ * Checks if selection is possible (browser platform and selectable is set)
1130
+ */
1131
+ canSelect() {
1132
+ return isPlatformBrowser(this.platformId) && !!this.selectable;
1133
+ }
1134
+ /**
1135
+ * Converts mouse event coordinates to coordinates relative to the container
1136
+ * Takes into account scroll position for scrollable containers.
1137
+ *
1138
+ * @param event - Mouse event with clientX/clientY
1139
+ * @returns Coordinates relative to the container's top-left corner (including scroll)
1140
+ */
1141
+ getRelativeCoordinates(event) {
1142
+ const containerElement = this.elementRef.nativeElement;
1143
+ const rect = containerElement.getBoundingClientRect();
1144
+ return {
1145
+ x: event.clientX - rect.left + containerElement.scrollLeft,
1146
+ y: event.clientY - rect.top + containerElement.scrollTop
1147
+ };
1148
+ }
1149
+ /**
1150
+ * Initiates a new selection
1151
+ *
1152
+ * @param coords - Relative coordinates where selection started
1153
+ * @param absoluteX - Absolute X coordinate (for element detection)
1154
+ * @param absoluteY - Absolute Y coordinate (for element detection)
1155
+ */
1156
+ startSelection(coords, absoluteX, absoluteY) {
1157
+ this.isSelecting.set(true);
1158
+ this.startPoint = coords;
1159
+ // Get the date/resource from the starting point
1160
+ if (!this.selectable)
1161
+ return;
1162
+ this.startData = this.selectable.getDateFromPoint(coords.x, coords.y);
1163
+ this.currentData = this.startData;
1164
+ // Initialize selection rectangle at a single point
1165
+ this.selection.set({
1166
+ top: coords.y,
1167
+ left: coords.x,
1168
+ bottom: coords.y,
1169
+ right: coords.x
1170
+ });
1171
+ // Emit selection start event
1172
+ this.selectionStart.emit({
1173
+ start: this.startData,
1174
+ end: this.currentData
1175
+ });
1176
+ }
1177
+ /**
1178
+ * Updates an ongoing selection
1179
+ *
1180
+ * @param coords - Current relative coordinates
1181
+ * @param absoluteX - Absolute X coordinate (for element detection)
1182
+ * @param absoluteY - Absolute Y coordinate (for element detection)
1183
+ */
1184
+ updateSelection(coords, absoluteX, absoluteY) {
1185
+ if (!this.startPoint)
1186
+ return;
1187
+ // Calculate selection rectangle (supports dragging in any direction)
1188
+ const rect = this.calculateSelectionRectangle(this.startPoint, coords);
1189
+ this.selection.set(rect);
1190
+ // Get the date/resource from the current point
1191
+ if (!this.selectable)
1192
+ return;
1193
+ this.currentData = this.selectable.getDateFromPoint(coords.x, coords.y);
1194
+ // Emit selection change event
1195
+ this.selectionChange.emit({
1196
+ start: this.startData,
1197
+ end: this.currentData
1198
+ });
1199
+ }
1200
+ /**
1201
+ * Completes the current selection
1202
+ */
1203
+ endSelection() {
1204
+ this.isSelecting.set(false);
1205
+ // Emit final selection event
1206
+ this.selectionEnd.emit({
1207
+ start: this.startData,
1208
+ end: this.currentData
1209
+ });
1210
+ // Clear selection state
1211
+ this.clearSelection();
1212
+ }
1213
+ /**
1214
+ * Calculates the selection rectangle from start and end points.
1215
+ * Handles dragging in any direction by using min/max.
1216
+ *
1217
+ * @param start - Starting point coordinates
1218
+ * @param end - Ending point coordinates
1219
+ * @returns Selection rectangle with top, left, bottom, right
1220
+ */
1221
+ calculateSelectionRectangle(start, end) {
1222
+ return {
1223
+ top: Math.min(start.y, end.y),
1224
+ left: Math.min(start.x, end.x),
1225
+ bottom: Math.max(start.y, end.y),
1226
+ right: Math.max(start.x, end.x)
1227
+ };
1228
+ }
1229
+ /**
1230
+ * Clears all selection state
1231
+ */
1232
+ clearSelection() {
1233
+ this.selection.set(null);
1234
+ this.startPoint = null;
1235
+ this.startData = null;
1236
+ this.currentData = null;
1237
+ }
1238
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SelectableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1239
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: SelectableDirective, isStandalone: true, selector: "[mglonSelectable]", inputs: { selectable: ["mglonSelectable", "selectable"] }, outputs: { selectionStart: "selectionStart", selectionChange: "selectionChange", selectionEnd: "selectionEnd" }, host: { listeners: { "mousedown": "onMouseDown($event)", "mousemove": "onMouseMove($event)", "mouseup": "onMouseUp($event)" } }, ngImport: i0 });
1240
+ }
1241
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SelectableDirective, decorators: [{
1242
+ type: Directive,
1243
+ args: [{
1244
+ selector: '[mglonSelectable]',
1245
+ standalone: true
1246
+ }]
1247
+ }], propDecorators: { selectable: [{
1248
+ type: Input,
1249
+ args: ['mglonSelectable']
1250
+ }], selectionStart: [{ type: i0.Output, args: ["selectionStart"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], selectionEnd: [{ type: i0.Output, args: ["selectionEnd"] }], onMouseDown: [{
1251
+ type: HostListener,
1252
+ args: ['mousedown', ['$event']]
1253
+ }], onMouseMove: [{
1254
+ type: HostListener,
1255
+ args: ['mousemove', ['$event']]
1256
+ }], onMouseUp: [{
1257
+ type: HostListener,
1258
+ args: ['mouseup', ['$event']]
1259
+ }] } });
1260
+
1261
+ /**
1262
+ * Week grid component displaying time slots for 7 days.
1263
+ *
1264
+ * Implements the Selectable interface to enable time-based selection.
1265
+ * Displays a scrollable grid of 30-minute time slots (48 per day).
1266
+ *
1267
+ * Features:
1268
+ * - 7 columns (days) × 48 rows (time slots)
1269
+ * - Scrollable container
1270
+ * - Mouse-based time selection
1271
+ * - 50px minimum height per slot
1272
+ *
1273
+ * @example
1274
+ * ```html
1275
+ * <mglon-week-grid
1276
+ * [currentDate]="selectedDate"
1277
+ * (selectionEnd)="onTimeRangeSelected($event)">
1278
+ * </mglon-week-grid>
1279
+ * ```
1280
+ */
1281
+ class WeekGrid {
1282
+ elementRef = inject(ElementRef);
1283
+ store = inject(CalendarStore);
1284
+ backgroundSelection = computed(() => this.store.config().backgroundSelection ?? true, ...(ngDevMode ? [{ debugName: "backgroundSelection" }] : []));
1285
+ showNowIndicator = computed(() => this.store.config().showNowIndicator ?? true, ...(ngDevMode ? [{ debugName: "showNowIndicator" }] : []));
1286
+ /**
1287
+ * Reference to the SelectableDirective instance
1288
+ */
1289
+ selectableDirective = viewChild(SelectableDirective, ...(ngDevMode ? [{ debugName: "selectableDirective" }] : []));
1290
+ /**
1291
+ * The date to display the week for
1292
+ */
1293
+ currentDate = input(new Date(), ...(ngDevMode ? [{ debugName: "currentDate" }] : []));
1294
+ /**
1295
+ * Selection events
1296
+ */
1297
+ selectionStart = output();
1298
+ selectionChange = output();
1299
+ selectionEnd = output();
1300
+ /**
1301
+ * Computed 2D array of time slots [days][slots]
1302
+ */
1303
+ timeSlots = computed(() => {
1304
+ return getWeekTimeSlots(this.currentDate());
1305
+ }, ...(ngDevMode ? [{ debugName: "timeSlots" }] : []));
1306
+ /**
1307
+ * Computed array of week days for column headers
1308
+ */
1309
+ weekDays = computed(() => {
1310
+ return getWeekDays(this.currentDate());
1311
+ }, ...(ngDevMode ? [{ debugName: "weekDays" }] : []));
1312
+ /**
1313
+ * Computed index of today's column (-1 if today is not in this week)
1314
+ */
1315
+ todayColumnIndex = computed(() => {
1316
+ const days = this.weekDays();
1317
+ return days.findIndex(day => day.isToday);
1318
+ }, ...(ngDevMode ? [{ debugName: "todayColumnIndex" }] : []));
1319
+ /**
1320
+ * Slot height in pixels (matches CSS min-height)
1321
+ */
1322
+ SLOT_HEIGHT = 50;
1323
+ /**
1324
+ * Time column width in pixels (matches CSS grid-template-columns)
1325
+ */
1326
+ TIME_COLUMN_WIDTH = 60;
1327
+ /**
1328
+ * Calculates the left position for the today indicator line
1329
+ * @returns Position in pixels from the left edge
1330
+ */
1331
+ getTodayIndicatorPosition() {
1332
+ const index = this.todayColumnIndex();
1333
+ if (index === -1)
1334
+ return 0;
1335
+ const gridElement = this.elementRef.nativeElement;
1336
+ if (!gridElement)
1337
+ return 0;
1338
+ const gridRect = gridElement.getBoundingClientRect();
1339
+ const dayColumnWidth = (gridRect.width - this.TIME_COLUMN_WIDTH - 8) / 7; // -8 for scrollbar
1340
+ // Position at the center of the column
1341
+ return this.TIME_COLUMN_WIDTH + (dayColumnWidth * index) + (dayColumnWidth / 2);
1342
+ }
1343
+ /**
1344
+ * Implements Selectable.getDateFromPoint()
1345
+ *
1346
+ * Translates visual coordinates into a specific date and time.
1347
+ *
1348
+ * Algorithm:
1349
+ * 1. Convert relative coordinates to absolute
1350
+ * 2. Determine which day column (0-6)
1351
+ * 3. Determine which time slot row (0-47)
1352
+ * 4. Calculate exact time (hour + minute)
1353
+ * 5. Return Date object with specific date and time
1354
+ *
1355
+ * @param x - X coordinate relative to the grid container
1356
+ * @param y - Y coordinate relative to the grid container
1357
+ * @returns Object with date property (including time), or null if invalid
1358
+ */
1359
+ getDateFromPoint(x, y) {
1360
+ const gridElement = this.elementRef.nativeElement;
1361
+ if (!gridElement)
1362
+ return null;
1363
+ // Account for time column
1364
+ const adjustedX = x - this.TIME_COLUMN_WIDTH;
1365
+ if (adjustedX < 0)
1366
+ return null; // Clicked on time column
1367
+ // Calculate which day column (0-6)
1368
+ const gridRect = gridElement.getBoundingClientRect();
1369
+ const dayColumnWidth = (gridRect.width - this.TIME_COLUMN_WIDTH) / 7;
1370
+ const dayIndex = Math.floor(adjustedX / dayColumnWidth);
1371
+ if (dayIndex < 0 || dayIndex > 6)
1372
+ return null;
1373
+ // Calculate which time slot (0-47)
1374
+ const slotIndex = Math.floor(y / this.SLOT_HEIGHT);
1375
+ if (slotIndex < 0 || slotIndex > 47)
1376
+ return null;
1377
+ // Get the time slot
1378
+ const timeSlots = this.timeSlots();
1379
+ if (!timeSlots[dayIndex] || !timeSlots[dayIndex][slotIndex])
1380
+ return null;
1381
+ return { date: timeSlots[dayIndex][slotIndex].date };
1382
+ }
1383
+ /**
1384
+ * Handles selection start event
1385
+ */
1386
+ onSelectionStart(result) {
1387
+ this.selectionStart.emit(result);
1388
+ }
1389
+ /**
1390
+ * Handles selection change event
1391
+ */
1392
+ onSelectionChange(result) {
1393
+ this.selectionChange.emit(result);
1394
+ }
1395
+ /**
1396
+ * Handles selection end event
1397
+ */
1398
+ onSelectionEnd(result) {
1399
+ this.selectionEnd.emit(result);
1400
+ }
1401
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WeekGrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
1402
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: WeekGrid, isStandalone: true, selector: "mglon-week-grid", inputs: { currentDate: { classPropertyName: "currentDate", publicName: "currentDate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionStart: "selectionStart", selectionChange: "selectionChange", selectionEnd: "selectionEnd" }, viewQueries: [{ propertyName: "selectableDirective", first: true, predicate: SelectableDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"mglon-week-grid\" [mglonSelectable]=\"backgroundSelection() ? this : null\"\n (selectionStart)=\"onSelectionStart($event)\" (selectionChange)=\"onSelectionChange($event)\"\n (selectionEnd)=\"onSelectionEnd($event)\">\n\n <!-- Time column -->\n <div class=\"mglon-week-grid__time-column\">\n @for (slot of timeSlots()[0]; track slot.date.getTime()) {\n <mglon-time-slot [slot]=\"slot\" [showTimeLabel]=\"true\"></mglon-time-slot>\n }\n </div>\n\n <!-- Day columns -->\n @for (daySlots of timeSlots(); track $index) {\n <div class=\"mglon-week-grid__day-column\">\n @for (slot of daySlots; track slot.date.getTime()) {\n <mglon-time-slot [slot]=\"slot\"></mglon-time-slot>\n }\n </div>\n }\n\n <!-- Selection overlay -->\n @if (selectableDirective() && backgroundSelection()) {\n <mglon-selection [selection]=\"selectableDirective()!.selection()\"\n [isSelecting]=\"selectableDirective()!.isSelecting()\">\n </mglon-selection>\n }\n\n <!-- Today indicator line -->\n @if (todayColumnIndex() !== -1 && showNowIndicator()) {\n <div class=\"mglon-week-grid__today-indicator\" [style.left.px]=\"getTodayIndicatorPosition()\">\n </div>\n }\n\n <!-- Events Overlay -->\n <div class=\"mglon-week-grid__events-overlay\">\n <ng-content></ng-content>\n </div>\n</div>", styles: [".mglon-week-grid{--week-grid-time-col-width: var(--mglon-week-grid-time-column-width, 60px);--week-grid-height: var(--mglon-week-grid-height, calc(100vh - 200px) );--week-grid-padding-top: var(--mglon-week-grid-padding-top, 12px);--week-grid-scrollbar-width: var(--mglon-week-grid-scrollbar-width, 8px);--week-grid-scrollbar-track-bg: var(--mglon-week-grid-scrollbar-track-bg, var(--mglon-schedule-surface-variant));--week-grid-scrollbar-thumb-bg: var(--mglon-week-grid-scrollbar-thumb-bg, var(--mglon-schedule-border-color));--week-grid-scrollbar-thumb-hover-bg: var(--mglon-week-grid-scrollbar-thumb-hover-bg, var(--mglon-schedule-on-surface-variant));--week-grid-time-col-bg: var(--mglon-week-grid-time-column-bg, var(--mglon-schedule-surface-variant));display:grid;grid-template-columns:var(--week-grid-time-col-width) repeat(7,1fr);position:relative;overflow-y:auto;overflow-x:hidden;max-height:var(--week-grid-height);-webkit-user-select:none;user-select:none;cursor:crosshair;padding-top:var(--week-grid-padding-top)}.mglon-week-grid::-webkit-scrollbar{width:var(--week-grid-scrollbar-width)}.mglon-week-grid::-webkit-scrollbar-track{background:var(--week-grid-scrollbar-track-bg)}.mglon-week-grid::-webkit-scrollbar-thumb{background:var(--week-grid-scrollbar-thumb-bg);border-radius:4px}.mglon-week-grid::-webkit-scrollbar-thumb:hover{background:var(--week-grid-scrollbar-thumb-hover-bg)}.mglon-week-grid__time-column{display:flex;flex-direction:column;border-right:1px solid var(--mglon-schedule-border);background:var(--week-grid-time-col-bg)}.mglon-week-grid__day-column{display:flex;flex-direction:column;border-right:1px solid var(--mglon-schedule-border)}.mglon-week-grid__day-column:last-child{border-right:none}.mglon-week-grid__today-indicator{--mglon-week-grid-today-color: var(--mglon-schedule-error);position:absolute;top:-60px;height:2460px;width:2px;background:var(--mglon-week-grid-today-color);pointer-events:none;z-index:5;transform:translate(-1px)}.mglon-week-grid__events-overlay{position:absolute;top:var(--week-grid-padding-top);left:var(--week-grid-time-col-width);right:0;bottom:0;pointer-events:none;transform:translate(-2px)}.mglon-week-grid__events-overlay>*{pointer-events:auto}\n"], dependencies: [{ kind: "component", type: TimeSlotComponent, selector: "mglon-time-slot", inputs: ["slot", "showTimeLabel"] }, { kind: "component", type: Selection, selector: "mglon-selection", inputs: ["selection", "isSelecting"] }, { kind: "directive", type: SelectableDirective, selector: "[mglonSelectable]", inputs: ["mglonSelectable"], outputs: ["selectionStart", "selectionChange", "selectionEnd"] }] });
1403
+ }
1404
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WeekGrid, decorators: [{
1405
+ type: Component,
1406
+ args: [{ selector: 'mglon-week-grid', standalone: true, imports: [TimeSlotComponent, Selection, SelectableDirective], template: "<div class=\"mglon-week-grid\" [mglonSelectable]=\"backgroundSelection() ? this : null\"\n (selectionStart)=\"onSelectionStart($event)\" (selectionChange)=\"onSelectionChange($event)\"\n (selectionEnd)=\"onSelectionEnd($event)\">\n\n <!-- Time column -->\n <div class=\"mglon-week-grid__time-column\">\n @for (slot of timeSlots()[0]; track slot.date.getTime()) {\n <mglon-time-slot [slot]=\"slot\" [showTimeLabel]=\"true\"></mglon-time-slot>\n }\n </div>\n\n <!-- Day columns -->\n @for (daySlots of timeSlots(); track $index) {\n <div class=\"mglon-week-grid__day-column\">\n @for (slot of daySlots; track slot.date.getTime()) {\n <mglon-time-slot [slot]=\"slot\"></mglon-time-slot>\n }\n </div>\n }\n\n <!-- Selection overlay -->\n @if (selectableDirective() && backgroundSelection()) {\n <mglon-selection [selection]=\"selectableDirective()!.selection()\"\n [isSelecting]=\"selectableDirective()!.isSelecting()\">\n </mglon-selection>\n }\n\n <!-- Today indicator line -->\n @if (todayColumnIndex() !== -1 && showNowIndicator()) {\n <div class=\"mglon-week-grid__today-indicator\" [style.left.px]=\"getTodayIndicatorPosition()\">\n </div>\n }\n\n <!-- Events Overlay -->\n <div class=\"mglon-week-grid__events-overlay\">\n <ng-content></ng-content>\n </div>\n</div>", styles: [".mglon-week-grid{--week-grid-time-col-width: var(--mglon-week-grid-time-column-width, 60px);--week-grid-height: var(--mglon-week-grid-height, calc(100vh - 200px) );--week-grid-padding-top: var(--mglon-week-grid-padding-top, 12px);--week-grid-scrollbar-width: var(--mglon-week-grid-scrollbar-width, 8px);--week-grid-scrollbar-track-bg: var(--mglon-week-grid-scrollbar-track-bg, var(--mglon-schedule-surface-variant));--week-grid-scrollbar-thumb-bg: var(--mglon-week-grid-scrollbar-thumb-bg, var(--mglon-schedule-border-color));--week-grid-scrollbar-thumb-hover-bg: var(--mglon-week-grid-scrollbar-thumb-hover-bg, var(--mglon-schedule-on-surface-variant));--week-grid-time-col-bg: var(--mglon-week-grid-time-column-bg, var(--mglon-schedule-surface-variant));display:grid;grid-template-columns:var(--week-grid-time-col-width) repeat(7,1fr);position:relative;overflow-y:auto;overflow-x:hidden;max-height:var(--week-grid-height);-webkit-user-select:none;user-select:none;cursor:crosshair;padding-top:var(--week-grid-padding-top)}.mglon-week-grid::-webkit-scrollbar{width:var(--week-grid-scrollbar-width)}.mglon-week-grid::-webkit-scrollbar-track{background:var(--week-grid-scrollbar-track-bg)}.mglon-week-grid::-webkit-scrollbar-thumb{background:var(--week-grid-scrollbar-thumb-bg);border-radius:4px}.mglon-week-grid::-webkit-scrollbar-thumb:hover{background:var(--week-grid-scrollbar-thumb-hover-bg)}.mglon-week-grid__time-column{display:flex;flex-direction:column;border-right:1px solid var(--mglon-schedule-border);background:var(--week-grid-time-col-bg)}.mglon-week-grid__day-column{display:flex;flex-direction:column;border-right:1px solid var(--mglon-schedule-border)}.mglon-week-grid__day-column:last-child{border-right:none}.mglon-week-grid__today-indicator{--mglon-week-grid-today-color: var(--mglon-schedule-error);position:absolute;top:-60px;height:2460px;width:2px;background:var(--mglon-week-grid-today-color);pointer-events:none;z-index:5;transform:translate(-1px)}.mglon-week-grid__events-overlay{position:absolute;top:var(--week-grid-padding-top);left:var(--week-grid-time-col-width);right:0;bottom:0;pointer-events:none;transform:translate(-2px)}.mglon-week-grid__events-overlay>*{pointer-events:auto}\n"] }]
1407
+ }], propDecorators: { selectableDirective: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SelectableDirective), { isSignal: true }] }], currentDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentDate", required: false }] }], selectionStart: [{ type: i0.Output, args: ["selectionStart"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], selectionEnd: [{ type: i0.Output, args: ["selectionEnd"] }] } });
1408
+
1409
+ /**
1410
+ * Week view container component.
1411
+ */
1412
+ class WeekView {
1413
+ store = inject(CalendarStore);
1414
+ // ============================================
1415
+ // OUTPUT EVENTS
1416
+ // ============================================
1417
+ /** Emitted when selection starts */
1418
+ selectionStart = output();
1419
+ /** Emitted while selecting */
1420
+ selectionChange = output();
1421
+ /** Emitted when selection ends */
1422
+ selectionEnd = output();
1423
+ // Get week grid element reference
1424
+ gridElement = viewChild(WeekGrid, { ...(ngDevMode ? { debugName: "gridElement" } : {}), read: ElementRef });
1425
+ animationState = computed(() => {
1426
+ const date = this.store.currentDate();
1427
+ return this.getWeekNumber(date);
1428
+ }, ...(ngDevMode ? [{ debugName: "animationState" }] : []));
1429
+ constructor() {
1430
+ afterNextRender(() => {
1431
+ // Logic for grid sizing can remain if needed for the grid itself,
1432
+ // but let's see if we can simplify it.
1433
+ });
1434
+ }
1435
+ getWeekNumber(date) {
1436
+ const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
1437
+ const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000;
1438
+ return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
1439
+ }
1440
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WeekView, deps: [], target: i0.ɵɵFactoryTarget.Component });
1441
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.6", type: WeekView, isStandalone: true, selector: "mglon-week-view", outputs: { selectionStart: "selectionStart", selectionChange: "selectionChange", selectionEnd: "selectionEnd" }, viewQueries: [{ propertyName: "gridElement", first: true, predicate: WeekGrid, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div class=\"mglon-week-view\" [@weekTransition]=\"animationState()\">\n <mglon-week-header [currentDate]=\"store.currentDate()\"></mglon-week-header>\n <mglon-week-grid [currentDate]=\"store.currentDate()\" (selectionStart)=\"selectionStart.emit($event)\"\n (selectionChange)=\"selectionChange.emit($event)\" (selectionEnd)=\"selectionEnd.emit($event)\">\n </mglon-week-grid>\n</div>", styles: [".mglon-week-view{--week-view-bg: var(--mglon-week-view-bg, var(--mglon-schedule-surface));display:flex;flex-direction:column;height:100%;background:var(--week-view-bg)}\n"], dependencies: [{ kind: "component", type: WeekHeader, selector: "mglon-week-header", inputs: ["currentDate"] }, { kind: "component", type: WeekGrid, selector: "mglon-week-grid", inputs: ["currentDate"], outputs: ["selectionStart", "selectionChange", "selectionEnd"] }], animations: [
1442
+ trigger('weekTransition', [
1443
+ transition('* => *', [
1444
+ style({ opacity: 0, transform: 'translateX({{ direction }}%)' }),
1445
+ animate('500ms ease-out', style({ opacity: 1, transform: 'translateX(0)' }))
1446
+ ], { params: { direction: 0 } })
1447
+ ])
1448
+ ] });
1449
+ }
1450
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WeekView, decorators: [{
1451
+ type: Component,
1452
+ args: [{ selector: 'mglon-week-view', standalone: true, imports: [WeekHeader, WeekGrid], animations: [
1453
+ trigger('weekTransition', [
1454
+ transition('* => *', [
1455
+ style({ opacity: 0, transform: 'translateX({{ direction }}%)' }),
1456
+ animate('500ms ease-out', style({ opacity: 1, transform: 'translateX(0)' }))
1457
+ ], { params: { direction: 0 } })
1458
+ ])
1459
+ ], template: "<div class=\"mglon-week-view\" [@weekTransition]=\"animationState()\">\n <mglon-week-header [currentDate]=\"store.currentDate()\"></mglon-week-header>\n <mglon-week-grid [currentDate]=\"store.currentDate()\" (selectionStart)=\"selectionStart.emit($event)\"\n (selectionChange)=\"selectionChange.emit($event)\" (selectionEnd)=\"selectionEnd.emit($event)\">\n </mglon-week-grid>\n</div>", styles: [".mglon-week-view{--week-view-bg: var(--mglon-week-view-bg, var(--mglon-schedule-surface));display:flex;flex-direction:column;height:100%;background:var(--week-view-bg)}\n"] }]
1460
+ }], ctorParameters: () => [], propDecorators: { selectionStart: [{ type: i0.Output, args: ["selectionStart"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], selectionEnd: [{ type: i0.Output, args: ["selectionEnd"] }], gridElement: [{ type: i0.ViewChild, args: [i0.forwardRef(() => WeekGrid), { ...{ read: ElementRef }, isSignal: true }] }] } });
1461
+
1462
+ const ICONS = {
1463
+ 'chevron-left': `
1464
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
1465
+ <path fill-rule="evenodd" d="M7.72 12.53a.75.75 0 010-1.06l7.5-7.5a.75.75 0 111.06 1.06L9.31 12l6.97 6.97a.75.75 0 11-1.06 1.06l-7.5-7.5z" clip-rule="evenodd" />
1466
+ </svg>`,
1467
+ 'chevron-right': `
1468
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
1469
+ <path fill-rule="evenodd" d="M16.28 11.47a.75.75 0 010 1.06l-7.5 7.5a.75.75 0 01-1.06-1.06L14.69 12 7.72 5.03a.75.75 0 011.06-1.06l7.5 7.5z" clip-rule="evenodd" />
1470
+ </svg>`,
1471
+ 'chevron-down': `
1472
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
1473
+ <path fill-rule="evenodd" d="M12.53 16.28a.75.75 0 01-1.06 0l-7.5-7.5a.75.75 0 011.06-1.06L12 14.69l6.97-6.97a.75.75 0 111.06 1.06l-7.5 7.5z" clip-rule="evenodd" />
1474
+ </svg>`,
1475
+ 'chevron-up': `
1476
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
1477
+ <path fill-rule="evenodd" d="M11.47 7.72a.75.75 0 011.06 0l7.5 7.5a.75.75 0 11-1.06 1.06L12 9.31l-6.97 6.97a.75.75 0 01-1.06-1.06l7.5-7.5z" clip-rule="evenodd" />
1478
+ </svg>`,
1479
+ 'clock': `
1480
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
1481
+ <path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zM12.75 6a.75.75 0 00-1.5 0v6c0 .414.336.75.75.75h4.5a.75.75 0 000-1.5h-3.75V6z" clip-rule="evenodd" />
1482
+ </svg>`,
1483
+ 'calendar-month': `
1484
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v560q0 33-23.5 56.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0 0v-80 80Zm280 240q-17 0-28.5-11.5T440-440q0-17 11.5-28.5T480-480q17 0 28.5 11.5T520-440q0 17-11.5 28.5T480-400Zm-160 0q-17 0-28.5-11.5T280-440q0-17 11.5-28.5T320-480q17 0 28.5 11.5T360-440q0 17-11.5 28.5T320-400Zm320 0q-17 0-28.5-11.5T600-440q0-17 11.5-28.5T640-480q17 0 28.5 11.5T680-440q0 17-11.5 28.5T640-400ZM480-240q-17 0-28.5-11.5T440-280q0-17 11.5-28.5T480-320q17 0 28.5 11.5T520-280q0 17-11.5 28.5T480-240Zm-160 0q-17 0-28.5-11.5T280-280q0-17 11.5-28.5T320-320q17 0 28.5 11.5T360-280q0 17-11.5 28.5T320-240Zm320 0q-17 0-28.5-11.5T600-280q0-17 11.5-28.5T640-320q17 0 28.5 11.5T680-280q0 17-11.5 28.5T640-240Z"/></svg>`,
1485
+ 'calendar-week': `<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm360-80h100v-480H520v480Zm-180 0h100v-480H340v480Zm-180 0h100v-480H160v480Zm540 0h100v-480H700v480Z"/></svg>`,
1486
+ 'calendar-day': `
1487
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M200-280q-33 0-56.5-23.5T120-360v-240q0-33 23.5-56.5T200-680h560q33 0 56.5 23.5T840-600v240q0 33-23.5 56.5T760-280H200Zm0-80h560v-240H200v240Zm-80-400v-80h720v80H120Zm0 640v-80h720v80H120Zm80-480v240-240Z"/></svg>`,
1488
+ 'add': `
1489
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
1490
+ <path fill-rule="evenodd" d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z" clip-rule="evenodd" />
1491
+ </svg>`,
1492
+ 'cycle': `
1493
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M204-318q-22-38-33-78t-11-82q0-134 93-228t227-94h7l-64-64 56-56 160 160-160 160-56-56 64-64h-7q-100 0-170 70.5T240-478q0 26 6 51t18 49l-60 60ZM481-40 321-200l160-160 56 56-64 64h7q100 0 170-70.5T720-482q0-26-6-51t-18-49l60-60q22 38 33 78t11 82q0 134-93 228t-227 94h-7l64 64-56 56Z"/></svg>
1494
+ `,
1495
+ 'dot': `
1496
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
1497
+ <circle cx="12" cy="12" r="6" />
1498
+ </svg>
1499
+ `
1500
+ };
1501
+
1502
+ class IconComponent {
1503
+ sanitizer = inject(DomSanitizer);
1504
+ // Input requerido tipo Signal
1505
+ name = input.required(...(ngDevMode ? [{ debugName: "name" }] : []));
1506
+ // Computada: Busca el string y lo sanitiza para evitar ataques XSS
1507
+ svgContent = computed(() => {
1508
+ const svgString = ICONS[this.name()] || '';
1509
+ return this.sanitizer.bypassSecurityTrustHtml(svgString);
1510
+ }, ...(ngDevMode ? [{ debugName: "svgContent" }] : []));
1511
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1512
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: IconComponent, isStandalone: true, selector: "mglon-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `<div class="mglon-icon" [innerHTML]="svgContent()"></div>`, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:1}.mglon-icon{display:flex;align-items:center;justify-content:center;width:1em;height:1em;min-width:1em;min-height:1em}::ng-deep svg{width:100%;height:100%;fill:currentColor}\n"] });
1513
+ }
1514
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: IconComponent, decorators: [{
1515
+ type: Component,
1516
+ args: [{ selector: 'mglon-icon', standalone: true, template: `<div class="mglon-icon" [innerHTML]="svgContent()"></div>`, styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:1}.mglon-icon{display:flex;align-items:center;justify-content:center;width:1em;height:1em;min-width:1em;min-height:1em}::ng-deep svg{width:100%;height:100%;fill:currentColor}\n"] }]
1517
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }] } });
1518
+
1519
+ class ButtonComponent {
1520
+ appereance = input('solid', ...(ngDevMode ? [{ debugName: "appereance" }] : []));
1521
+ color = input('primary', ...(ngDevMode ? [{ debugName: "color" }] : []));
1522
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
1523
+ rounded = input('md', ...(ngDevMode ? [{ debugName: "rounded" }] : []));
1524
+ density = input('comfortable', ...(ngDevMode ? [{ debugName: "density" }] : []));
1525
+ active = input(false, ...(ngDevMode ? [{ debugName: "active" }] : []));
1526
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1527
+ elementRef = inject(ElementRef);
1528
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1529
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: ButtonComponent, isStandalone: true, selector: "button[mglon-button], a[mglon-button]", inputs: { appereance: { classPropertyName: "appereance", publicName: "appereance", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.appereance": "appereance()", "attr.color": "color()", "attr.size": "size()", "attr.rounded": "rounded()", "attr.density": "density()", "attr.active": "active() || null", "attr.disabled": "disabled() || null" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;position:relative;box-sizing:border-box;min-width:var(--mglon-button-min-width, 64px);outline:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;vertical-align:middle;text-decoration:none;cursor:pointer;--btn-padding-y: var(--mglon-button-padding-y, 0);--btn-padding-x: var(--mglon-button-padding-x, 16px);--btn-height: var(--mglon-button-height, 40px);--btn-radius: var(--mglon-button-radius, var(--mglon-schedule-radius-md));--btn-font-size: var(--mglon-button-font-size, var(--mglon-schedule-font-size-md, 14px));--btn-border-width: var(--mglon-button-border-width, 2px);--btn-transition: var(--mglon-button-transition, background-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing), box-shadow .28s var(--mglon-schedule-transition-easing), color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing), border-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing));height:var(--btn-height);padding:var(--btn-padding-y) var(--btn-padding-x);border-radius:var(--btn-radius);font-size:var(--btn-font-size);font-family:var(--mglon-schedule-font-family, Roboto, sans-serif);font-weight:var(--mglon-button-font-weight, 500);border:var(--btn-border-width) solid transparent;background-color:var(--mglon-button-primary-bg, var(--mglon-schedule-primary));color:var(--mglon-button-primary-color, var(--mglon-schedule-on-primary));transition:var(--btn-transition)}:host[rounded=none]{--btn-radius: 0}:host[rounded=sm]{--btn-radius: var(--mglon-schedule-radius-sm)}:host[rounded=md]{--btn-radius: var(--mglon-schedule-radius-md)}:host[rounded=lg]{--btn-radius: var(--mglon-schedule-radius-lg)}:host[rounded=full]{--btn-radius: var(--mglon-schedule-radius-full)}:host[size=sm]{--btn-height: var(--mglon-button-sm-height, 32px);--btn-padding-x: var(--mglon-button-sm-padding-x, 12px);--btn-font-size: var(--mglon-button-sm-font-size, var(--mglon-schedule-font-size-sm, 12px))}:host[size=md]{--btn-height: var(--mglon-button-md-height, 40px);--btn-padding-x: var(--mglon-button-md-padding-x, 16px);--btn-font-size: var(--mglon-button-md-font-size, var(--mglon-schedule-font-size-md, 14px))}:host[size=lg]{--btn-height: var(--mglon-button-lg-height, 48px);--btn-padding-x: var(--mglon-button-lg-padding-x, 24px);--btn-font-size: var(--mglon-button-lg-font-size, var(--mglon-schedule-font-size-lg, 16px))}:host[density=compact]{--btn-padding-x: var(--mglon-button-compact-padding-x, 8px)}:host[appereance=solid]{box-shadow:0 1px 3px #0000001a,0 1px 2px #0000000f}:host[appereance=solid]:hover:not([disabled]){box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;opacity:var(--mglon-button-hover-opacity, .92)}:host[appereance=outline]{background-color:transparent;border-color:currentColor}:host[appereance=outline]:hover:not([disabled]){background-color:var(--mglon-button-hover-bg, var(--mglon-schedule-hover-bg))}:host[appereance=ghost]{background-color:transparent;border-color:transparent}:host[appereance=ghost]:hover:not([disabled]){background-color:var(--mglon-button-hover-bg, var(--mglon-schedule-hover-bg))}:host[appereance=link]{background-color:transparent;border-color:transparent;text-decoration:underline;min-width:auto;--btn-padding-x: 4px}:host[appereance=link]:hover:not([disabled]){text-decoration:none}:host[appereance=solid][color=primary]{background-color:var(--mglon-button-primary-bg, var(--mglon-schedule-primary));color:var(--mglon-button-primary-color, var(--mglon-schedule-on-primary))}:host[appereance=solid][color=secondary]{background-color:var(--mglon-button-secondary-bg, var(--mglon-schedule-surface-3));color:var(--mglon-button-secondary-color, var(--mglon-schedule-on-surface-3))}:host[appereance=solid][color=error]{background-color:var(--mglon-button-error-bg, var(--mglon-schedule-error));color:var(--mglon-button-error-color, var(--mglon-schedule-on-error))}:host[appereance=outline][color=primary]{color:var(--mglon-button-primary-bg, var(--mglon-schedule-primary));border-color:var(--mglon-button-primary-bg, var(--mglon-schedule-primary))}:host[appereance=outline][color=primary]:hover:not([disabled]){background-color:var(--mglon-button-primary-hover-bg, rgba(103, 58, 183, .04))}:host[appereance=outline][color=secondary]{color:var(--mglon-schedule-text-secondary);border-color:var(--mglon-schedule-text-secondary)}:host[appereance=outline][color=error]{color:var(--mglon-schedule-error);border-color:var(--mglon-schedule-error)}:host[appereance=ghost][color=primary]{color:var(--mglon-schedule-primary)}:host[appereance=ghost][color=primary]:hover:not([disabled]){background-color:var(--mglon-button-primary-hover-bg, rgba(103, 58, 183, .04))}:host[appereance=ghost][color=secondary]{color:var(--mglon-schedule-text-secondary)}:host[appereance=ghost][color=error]{color:var(--mglon-schedule-error)}:host[appereance=link][color=primary]{color:var(--mglon-schedule-primary)}:host[appereance=link][color=secondary]{color:var(--mglon-schedule-text-secondary)}:host[appereance=link][color=error]{color:var(--mglon-schedule-error)}:host[active]{background-color:var(--mglon-schedule-primary);color:var(--mglon-schedule-on-primary);font-weight:600}:host[active][appereance=outline],:host[active][appereance=ghost]{background-color:var(--mglon-schedule-selection);color:var(--mglon-schedule-primary)}:host[disabled]{opacity:var(--mglon-schedule-disabled-opacity, .5);cursor:not-allowed;pointer-events:none;box-shadow:none!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1530
+ }
1531
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ButtonComponent, decorators: [{
1532
+ type: Component,
1533
+ args: [{ selector: 'button[mglon-button], a[mglon-button]', standalone: true, imports: [CommonModule], template: '<ng-content></ng-content>', host: {
1534
+ '[attr.appereance]': 'appereance()',
1535
+ '[attr.color]': 'color()',
1536
+ '[attr.size]': 'size()',
1537
+ '[attr.rounded]': 'rounded()',
1538
+ '[attr.density]': 'density()',
1539
+ '[attr.active]': 'active() || null',
1540
+ '[attr.disabled]': 'disabled() || null'
1541
+ }, styles: [":host{display:inline-flex;align-items:center;justify-content:center;position:relative;box-sizing:border-box;min-width:var(--mglon-button-min-width, 64px);outline:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;vertical-align:middle;text-decoration:none;cursor:pointer;--btn-padding-y: var(--mglon-button-padding-y, 0);--btn-padding-x: var(--mglon-button-padding-x, 16px);--btn-height: var(--mglon-button-height, 40px);--btn-radius: var(--mglon-button-radius, var(--mglon-schedule-radius-md));--btn-font-size: var(--mglon-button-font-size, var(--mglon-schedule-font-size-md, 14px));--btn-border-width: var(--mglon-button-border-width, 2px);--btn-transition: var(--mglon-button-transition, background-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing), box-shadow .28s var(--mglon-schedule-transition-easing), color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing), border-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing));height:var(--btn-height);padding:var(--btn-padding-y) var(--btn-padding-x);border-radius:var(--btn-radius);font-size:var(--btn-font-size);font-family:var(--mglon-schedule-font-family, Roboto, sans-serif);font-weight:var(--mglon-button-font-weight, 500);border:var(--btn-border-width) solid transparent;background-color:var(--mglon-button-primary-bg, var(--mglon-schedule-primary));color:var(--mglon-button-primary-color, var(--mglon-schedule-on-primary));transition:var(--btn-transition)}:host[rounded=none]{--btn-radius: 0}:host[rounded=sm]{--btn-radius: var(--mglon-schedule-radius-sm)}:host[rounded=md]{--btn-radius: var(--mglon-schedule-radius-md)}:host[rounded=lg]{--btn-radius: var(--mglon-schedule-radius-lg)}:host[rounded=full]{--btn-radius: var(--mglon-schedule-radius-full)}:host[size=sm]{--btn-height: var(--mglon-button-sm-height, 32px);--btn-padding-x: var(--mglon-button-sm-padding-x, 12px);--btn-font-size: var(--mglon-button-sm-font-size, var(--mglon-schedule-font-size-sm, 12px))}:host[size=md]{--btn-height: var(--mglon-button-md-height, 40px);--btn-padding-x: var(--mglon-button-md-padding-x, 16px);--btn-font-size: var(--mglon-button-md-font-size, var(--mglon-schedule-font-size-md, 14px))}:host[size=lg]{--btn-height: var(--mglon-button-lg-height, 48px);--btn-padding-x: var(--mglon-button-lg-padding-x, 24px);--btn-font-size: var(--mglon-button-lg-font-size, var(--mglon-schedule-font-size-lg, 16px))}:host[density=compact]{--btn-padding-x: var(--mglon-button-compact-padding-x, 8px)}:host[appereance=solid]{box-shadow:0 1px 3px #0000001a,0 1px 2px #0000000f}:host[appereance=solid]:hover:not([disabled]){box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;opacity:var(--mglon-button-hover-opacity, .92)}:host[appereance=outline]{background-color:transparent;border-color:currentColor}:host[appereance=outline]:hover:not([disabled]){background-color:var(--mglon-button-hover-bg, var(--mglon-schedule-hover-bg))}:host[appereance=ghost]{background-color:transparent;border-color:transparent}:host[appereance=ghost]:hover:not([disabled]){background-color:var(--mglon-button-hover-bg, var(--mglon-schedule-hover-bg))}:host[appereance=link]{background-color:transparent;border-color:transparent;text-decoration:underline;min-width:auto;--btn-padding-x: 4px}:host[appereance=link]:hover:not([disabled]){text-decoration:none}:host[appereance=solid][color=primary]{background-color:var(--mglon-button-primary-bg, var(--mglon-schedule-primary));color:var(--mglon-button-primary-color, var(--mglon-schedule-on-primary))}:host[appereance=solid][color=secondary]{background-color:var(--mglon-button-secondary-bg, var(--mglon-schedule-surface-3));color:var(--mglon-button-secondary-color, var(--mglon-schedule-on-surface-3))}:host[appereance=solid][color=error]{background-color:var(--mglon-button-error-bg, var(--mglon-schedule-error));color:var(--mglon-button-error-color, var(--mglon-schedule-on-error))}:host[appereance=outline][color=primary]{color:var(--mglon-button-primary-bg, var(--mglon-schedule-primary));border-color:var(--mglon-button-primary-bg, var(--mglon-schedule-primary))}:host[appereance=outline][color=primary]:hover:not([disabled]){background-color:var(--mglon-button-primary-hover-bg, rgba(103, 58, 183, .04))}:host[appereance=outline][color=secondary]{color:var(--mglon-schedule-text-secondary);border-color:var(--mglon-schedule-text-secondary)}:host[appereance=outline][color=error]{color:var(--mglon-schedule-error);border-color:var(--mglon-schedule-error)}:host[appereance=ghost][color=primary]{color:var(--mglon-schedule-primary)}:host[appereance=ghost][color=primary]:hover:not([disabled]){background-color:var(--mglon-button-primary-hover-bg, rgba(103, 58, 183, .04))}:host[appereance=ghost][color=secondary]{color:var(--mglon-schedule-text-secondary)}:host[appereance=ghost][color=error]{color:var(--mglon-schedule-error)}:host[appereance=link][color=primary]{color:var(--mglon-schedule-primary)}:host[appereance=link][color=secondary]{color:var(--mglon-schedule-text-secondary)}:host[appereance=link][color=error]{color:var(--mglon-schedule-error)}:host[active]{background-color:var(--mglon-schedule-primary);color:var(--mglon-schedule-on-primary);font-weight:600}:host[active][appereance=outline],:host[active][appereance=ghost]{background-color:var(--mglon-schedule-selection);color:var(--mglon-schedule-primary)}:host[disabled]{opacity:var(--mglon-schedule-disabled-opacity, .5);cursor:not-allowed;pointer-events:none;box-shadow:none!important}\n"] }]
1542
+ }], propDecorators: { appereance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appereance", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
1543
+
1544
+ class IconButtonComponent {
1545
+ appereance = input('ghost', ...(ngDevMode ? [{ debugName: "appereance" }] : []));
1546
+ color = input('primary', ...(ngDevMode ? [{ debugName: "color" }] : []));
1547
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
1548
+ rounded = input('md', ...(ngDevMode ? [{ debugName: "rounded" }] : []));
1549
+ active = input(false, ...(ngDevMode ? [{ debugName: "active" }] : []));
1550
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1551
+ elementRef = inject(ElementRef);
1552
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: IconButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1553
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: IconButtonComponent, isStandalone: true, selector: "button[mglon-icon-button], a[mglon-icon-button]", inputs: { appereance: { classPropertyName: "appereance", publicName: "appereance", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.appereance": "appereance()", "attr.color": "color()", "attr.size": "size()", "attr.rounded": "rounded()", "attr.active": "active() || null", "attr.disabled": "disabled() || null" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;position:relative;box-sizing:border-box;outline:none;overflow:hidden;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;--icon-btn-size: var(--mglon-icon-button-size, 40px);--icon-btn-icon-size: var(--mglon-icon-button-icon-size, 24px);--icon-btn-radius: var(--mglon-icon-button-radius, 50%);--icon-btn-bg: var(--mglon-icon-button-bg, transparent);--icon-btn-color: var(--mglon-icon-button-color, var(--mglon-schedule-text-secondary));--icon-btn-border: var(--mglon-icon-button-border, 2px solid transparent);--icon-btn-transition: var(--mglon-icon-button-transition, background-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing), color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing), border-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing));width:var(--icon-btn-size);height:var(--icon-btn-size);font-size:var(--icon-btn-icon-size);border-radius:var(--icon-btn-radius);border:var(--icon-btn-border);background-color:var(--icon-btn-bg);color:var(--icon-btn-color);transition:var(--icon-btn-transition)}:host[size=xs]{--icon-btn-size: var(--mglon-icon-button-xs-size, 24px);--icon-btn-icon-size: var(--mglon-icon-button-xs-icon-size, 14px)}:host[size=sm]{--icon-btn-size: var(--mglon-icon-button-sm-size, 32px);--icon-btn-icon-size: var(--mglon-icon-button-sm-icon-size, 18px)}:host[size=md]{--icon-btn-size: var(--mglon-icon-button-md-size, 40px);--icon-btn-icon-size: var(--mglon-icon-button-md-icon-size, 24px)}:host[size=lg]{--icon-btn-size: var(--mglon-icon-button-lg-size, 48px);--icon-btn-icon-size: var(--mglon-icon-button-lg-icon-size, 28px)}:host[appereance=ghost]{background-color:transparent;border-color:transparent}:host[appereance=ghost]:hover:not([disabled]){background-color:var(--mglon-icon-button-hover-bg, rgba(60, 64, 67, .04))}:host[appereance=solid]{box-shadow:0 1px 3px #0000001a,0 1px 2px #0000000f}:host[appereance=solid]:hover:not([disabled]){box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;opacity:var(--mglon-icon-button-hover-opacity, .92)}:host[appereance=outline]{background-color:transparent;border-color:currentColor}:host[appereance=outline]:hover:not([disabled]){background-color:var(--mglon-icon-button-hover-bg, var(--mglon-schedule-hover-bg))}:host[appereance=ghost][color=primary]{color:var(--mglon-schedule-text-secondary)}:host[appereance=ghost][color=primary]:hover:not([disabled]){color:var(--mglon-schedule-text-primary);background-color:var(--mglon-icon-button-primary-hover-bg, rgba(103, 58, 183, .04))}:host[appereance=ghost][color=secondary]{color:var(--mglon-schedule-text-secondary)}:host[appereance=ghost][color=error]{color:var(--mglon-schedule-error)}:host[appereance=solid][color=primary]{background-color:var(--mglon-icon-button-primary-bg, var(--mglon-schedule-primary));color:var(--mglon-icon-button-primary-color, var(--mglon-schedule-on-primary))}:host[appereance=solid][color=secondary]{background-color:var(--mglon-icon-button-secondary-bg, var(--mglon-schedule-surface-3));color:var(--mglon-icon-button-secondary-color, var(--mglon-schedule-on-surface-3))}:host[appereance=solid][color=error]{background-color:var(--mglon-icon-button-error-bg, var(--mglon-schedule-error));color:var(--mglon-icon-button-error-color, var(--mglon-schedule-on-error))}:host[appereance=outline][color=primary]{color:var(--mglon-icon-button-primary-color, var(--mglon-schedule-primary));border-color:var(--mglon-icon-button-primary-color, var(--mglon-schedule-primary))}:host[appereance=outline][color=primary]:hover:not([disabled]){background-color:var(--mglon-icon-button-primary-hover-bg, rgba(103, 58, 183, .04))}:host[appereance=outline][color=secondary]{color:var(--mglon-schedule-text-secondary);border-color:var(--mglon-schedule-text-secondary)}:host[appereance=outline][color=error]{color:var(--mglon-schedule-error);border-color:var(--mglon-schedule-error)}:host[active]{color:var(--mglon-schedule-primary);background-color:var(--mglon-schedule-selection);font-weight:600}:host[active][appereance=solid]{background-color:var(--mglon-schedule-primary);color:var(--mglon-schedule-on-primary)}:host[disabled]{opacity:var(--mglon-schedule-disabled-opacity, .5);cursor:not-allowed;pointer-events:none;background-color:transparent!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1554
+ }
1555
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: IconButtonComponent, decorators: [{
1556
+ type: Component,
1557
+ args: [{ selector: 'button[mglon-icon-button], a[mglon-icon-button]', standalone: true, imports: [CommonModule], template: '<ng-content></ng-content>', host: {
1558
+ '[attr.appereance]': 'appereance()',
1559
+ '[attr.color]': 'color()',
1560
+ '[attr.size]': 'size()',
1561
+ '[attr.rounded]': 'rounded()',
1562
+ '[attr.active]': 'active() || null',
1563
+ '[attr.disabled]': 'disabled() || null'
1564
+ }, styles: [":host{display:inline-flex;align-items:center;justify-content:center;position:relative;box-sizing:border-box;outline:none;overflow:hidden;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;--icon-btn-size: var(--mglon-icon-button-size, 40px);--icon-btn-icon-size: var(--mglon-icon-button-icon-size, 24px);--icon-btn-radius: var(--mglon-icon-button-radius, 50%);--icon-btn-bg: var(--mglon-icon-button-bg, transparent);--icon-btn-color: var(--mglon-icon-button-color, var(--mglon-schedule-text-secondary));--icon-btn-border: var(--mglon-icon-button-border, 2px solid transparent);--icon-btn-transition: var(--mglon-icon-button-transition, background-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing), color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing), border-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing));width:var(--icon-btn-size);height:var(--icon-btn-size);font-size:var(--icon-btn-icon-size);border-radius:var(--icon-btn-radius);border:var(--icon-btn-border);background-color:var(--icon-btn-bg);color:var(--icon-btn-color);transition:var(--icon-btn-transition)}:host[size=xs]{--icon-btn-size: var(--mglon-icon-button-xs-size, 24px);--icon-btn-icon-size: var(--mglon-icon-button-xs-icon-size, 14px)}:host[size=sm]{--icon-btn-size: var(--mglon-icon-button-sm-size, 32px);--icon-btn-icon-size: var(--mglon-icon-button-sm-icon-size, 18px)}:host[size=md]{--icon-btn-size: var(--mglon-icon-button-md-size, 40px);--icon-btn-icon-size: var(--mglon-icon-button-md-icon-size, 24px)}:host[size=lg]{--icon-btn-size: var(--mglon-icon-button-lg-size, 48px);--icon-btn-icon-size: var(--mglon-icon-button-lg-icon-size, 28px)}:host[appereance=ghost]{background-color:transparent;border-color:transparent}:host[appereance=ghost]:hover:not([disabled]){background-color:var(--mglon-icon-button-hover-bg, rgba(60, 64, 67, .04))}:host[appereance=solid]{box-shadow:0 1px 3px #0000001a,0 1px 2px #0000000f}:host[appereance=solid]:hover:not([disabled]){box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;opacity:var(--mglon-icon-button-hover-opacity, .92)}:host[appereance=outline]{background-color:transparent;border-color:currentColor}:host[appereance=outline]:hover:not([disabled]){background-color:var(--mglon-icon-button-hover-bg, var(--mglon-schedule-hover-bg))}:host[appereance=ghost][color=primary]{color:var(--mglon-schedule-text-secondary)}:host[appereance=ghost][color=primary]:hover:not([disabled]){color:var(--mglon-schedule-text-primary);background-color:var(--mglon-icon-button-primary-hover-bg, rgba(103, 58, 183, .04))}:host[appereance=ghost][color=secondary]{color:var(--mglon-schedule-text-secondary)}:host[appereance=ghost][color=error]{color:var(--mglon-schedule-error)}:host[appereance=solid][color=primary]{background-color:var(--mglon-icon-button-primary-bg, var(--mglon-schedule-primary));color:var(--mglon-icon-button-primary-color, var(--mglon-schedule-on-primary))}:host[appereance=solid][color=secondary]{background-color:var(--mglon-icon-button-secondary-bg, var(--mglon-schedule-surface-3));color:var(--mglon-icon-button-secondary-color, var(--mglon-schedule-on-surface-3))}:host[appereance=solid][color=error]{background-color:var(--mglon-icon-button-error-bg, var(--mglon-schedule-error));color:var(--mglon-icon-button-error-color, var(--mglon-schedule-on-error))}:host[appereance=outline][color=primary]{color:var(--mglon-icon-button-primary-color, var(--mglon-schedule-primary));border-color:var(--mglon-icon-button-primary-color, var(--mglon-schedule-primary))}:host[appereance=outline][color=primary]:hover:not([disabled]){background-color:var(--mglon-icon-button-primary-hover-bg, rgba(103, 58, 183, .04))}:host[appereance=outline][color=secondary]{color:var(--mglon-schedule-text-secondary);border-color:var(--mglon-schedule-text-secondary)}:host[appereance=outline][color=error]{color:var(--mglon-schedule-error);border-color:var(--mglon-schedule-error)}:host[active]{color:var(--mglon-schedule-primary);background-color:var(--mglon-schedule-selection);font-weight:600}:host[active][appereance=solid]{background-color:var(--mglon-schedule-primary);color:var(--mglon-schedule-on-primary)}:host[disabled]{opacity:var(--mglon-schedule-disabled-opacity, .5);cursor:not-allowed;pointer-events:none;background-color:transparent!important}\n"] }]
1565
+ }], propDecorators: { appereance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appereance", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
1566
+
1567
+ class ButtonGroupComponent {
1568
+ type = input('text', ...(ngDevMode ? [{ debugName: "type" }] : []));
1569
+ density = input('comfortable', ...(ngDevMode ? [{ debugName: "density" }] : []));
1570
+ appereance = input('solid', ...(ngDevMode ? [{ debugName: "appereance" }] : []));
1571
+ rounded = input('full', ...(ngDevMode ? [{ debugName: "rounded" }] : []));
1572
+ orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
1573
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1574
+ // Queries for projected content
1575
+ buttons = contentChildren(ButtonComponent, ...(ngDevMode ? [{ debugName: "buttons" }] : []));
1576
+ iconButtons = contentChildren(IconButtonComponent, ...(ngDevMode ? [{ debugName: "iconButtons" }] : []));
1577
+ indicatorStyle = signal({}, ...(ngDevMode ? [{ debugName: "indicatorStyle" }] : []));
1578
+ constructor() {
1579
+ // Effect to update selection indicator position
1580
+ effect(() => {
1581
+ const allButtons = [...this.buttons(), ...this.iconButtons()];
1582
+ const activeButton = allButtons.find(btn => btn.active() && !btn.disabled());
1583
+ if (activeButton && activeButton.elementRef) {
1584
+ const element = activeButton.elementRef.nativeElement;
1585
+ // Calculate style based on the active element's position and size
1586
+ const rounded = this.rounded();
1587
+ this.indicatorStyle.set({
1588
+ 'width': `${element.offsetWidth}px`,
1589
+ 'height': `${element.offsetHeight}px`,
1590
+ 'transform': `translate(${element.offsetLeft}px, ${element.offsetTop}px)`,
1591
+ 'opacity': '1'
1592
+ });
1593
+ }
1594
+ else {
1595
+ this.indicatorStyle.set({ 'opacity': '0' });
1596
+ }
1597
+ });
1598
+ // Effect to propagate disabled state to child buttons
1599
+ effect(() => {
1600
+ const isGroupDisabled = this.disabled();
1601
+ const allButtons = [...this.buttons(), ...this.iconButtons()];
1602
+ allButtons.forEach(btn => {
1603
+ if (btn.elementRef) {
1604
+ const element = btn.elementRef.nativeElement;
1605
+ if (isGroupDisabled) {
1606
+ element.setAttribute('disabled', '');
1607
+ }
1608
+ else if (!btn.disabled()) {
1609
+ element.removeAttribute('disabled');
1610
+ }
1611
+ }
1612
+ });
1613
+ });
1614
+ }
1615
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ButtonGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1616
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.6", type: ButtonGroupComponent, isStandalone: true, selector: "mglon-button-group", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, appereance: { classPropertyName: "appereance", publicName: "appereance", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.role": "\"group\"", "attr.rounded": "rounded()", "attr.type": "type()", "attr.orientation": "orientation()", "attr.appereance": "appereance()", "attr.density": "density()", "attr.disabled": "disabled() || null" } }, queries: [{ propertyName: "buttons", predicate: ButtonComponent, isSignal: true }, { propertyName: "iconButtons", predicate: IconButtonComponent, isSignal: true }], ngImport: i0, template: "<div class=\"selection-indicator\" [style]=\"indicatorStyle()\"></div>\n<ng-content></ng-content>", styles: [":host{display:inline-flex;position:relative;overflow:hidden;z-index:0;gap:var(--mglon-button-group-gap, 0);flex-shrink:0;background-color:var(--mglon-button-group-background, var(--mglon-schedule-surface-1));border:var(--mglon-button-group-border-width, 2px) solid var(--mglon-button-group-border-color, transparent);padding:var(--mglon-button-group-padding, var(--mglon-schedule-padding-xs));border-radius:var(--mglon-button-group-border-radius, var(--mglon-schedule-radius-full))}:host[rounded=none]{--mglon-button-group-border-radius: 0}:host[rounded=sm]{--mglon-button-group-border-radius: var(--mglon-schedule-radius-sm)}:host[rounded=md]{--mglon-button-group-border-radius: var(--mglon-schedule-radius-md)}:host[rounded=lg]{--mglon-button-group-border-radius: var(--mglon-schedule-radius-lg)}:host[rounded=full]{--mglon-button-group-border-radius: var(--mglon-schedule-radius-full)}:host[orientation=vertical]{flex-direction:column}:host[appereance=outline]{--mglon-button-group-background: transparent;--mglon-button-group-border-color: var(--mglon-schedule-primary-200)}:host[appereance=ghost]{--mglon-button-group-background: transparent;--mglon-button-group-border-color: transparent;--mglon-button-group-padding: 0px}:host[appereance=link]{--mglon-button-group-background: transparent;--mglon-button-group-border-color: transparent;--mglon-button-group-padding: 0px;--mglon-button-group-gap: var(--mglon-schedule-spacing-sm)}:host[density=compact]{--mglon-button-group-padding: 0px}:host[density=compact][appereance=link]{--mglon-button-group-gap: var(--mglon-schedule-spacing-xs)}:host[disabled]{opacity:var(--mglon-button-group-disabled-opacity, .5);cursor:not-allowed;pointer-events:none}:host[disabled] .selection-indicator{pointer-events:none}:host[disabled] button,:host[disabled] a{pointer-events:none}:host .selection-indicator{position:absolute;top:0;left:0;background-color:var(--mglon-button-group-indicator-background, var(--mglon-schedule-primary));border-radius:calc(var(--mglon-button-group-border-radius, var(--mglon-schedule-radius-full)) - var(--mglon-button-group-padding, var(--mglon-schedule-padding-xs)));z-index:0;transition:all var(--mglon-button-group-indicator-transition-duration, var(--mglon-schedule-transition-duration)) cubic-bezier(.16,1,.3,1)}:host ::ng-deep>button[mglon-button],:host ::ng-deep>button[mglon-icon-button],:host ::ng-deep>a[mglon-button],:host ::ng-deep>a[mglon-icon-button]{background-color:transparent!important;border:none!important;margin:0!important;position:relative;z-index:1;border-radius:var(--mglon-button-group-border-radius, var(--mglon-schedule-radius-full))!important;color:var(--mglon-button-group-button-color, var(--mglon-schedule-on-surface-1));transition:color var(--mglon-button-group-transition-duration, var(--mglon-schedule-transition-duration)) ease}:host ::ng-deep>button[mglon-button][active],:host ::ng-deep>button[mglon-icon-button][active],:host ::ng-deep>a[mglon-button][active],:host ::ng-deep>a[mglon-icon-button][active]{color:var(--mglon-button-group-button-active-color, var(--mglon-schedule-on-primary))!important;font-weight:var(--mglon-button-group-button-active-font-weight, 500)}:host ::ng-deep>button[mglon-button][disabled],:host ::ng-deep>button[mglon-icon-button][disabled],:host ::ng-deep>a[mglon-button][disabled],:host ::ng-deep>a[mglon-icon-button][disabled]{opacity:var(--mglon-button-group-disabled-opacity, .5);cursor:not-allowed;pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1617
+ }
1618
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ButtonGroupComponent, decorators: [{
1619
+ type: Component,
1620
+ args: [{ selector: 'mglon-button-group', standalone: true, imports: [CommonModule], host: {
1621
+ '[attr.role]': '"group"',
1622
+ '[attr.rounded]': 'rounded()',
1623
+ '[attr.type]': 'type()',
1624
+ '[attr.orientation]': 'orientation()',
1625
+ '[attr.appereance]': 'appereance()',
1626
+ '[attr.density]': 'density()',
1627
+ '[attr.disabled]': 'disabled() || null'
1628
+ }, template: "<div class=\"selection-indicator\" [style]=\"indicatorStyle()\"></div>\n<ng-content></ng-content>", styles: [":host{display:inline-flex;position:relative;overflow:hidden;z-index:0;gap:var(--mglon-button-group-gap, 0);flex-shrink:0;background-color:var(--mglon-button-group-background, var(--mglon-schedule-surface-1));border:var(--mglon-button-group-border-width, 2px) solid var(--mglon-button-group-border-color, transparent);padding:var(--mglon-button-group-padding, var(--mglon-schedule-padding-xs));border-radius:var(--mglon-button-group-border-radius, var(--mglon-schedule-radius-full))}:host[rounded=none]{--mglon-button-group-border-radius: 0}:host[rounded=sm]{--mglon-button-group-border-radius: var(--mglon-schedule-radius-sm)}:host[rounded=md]{--mglon-button-group-border-radius: var(--mglon-schedule-radius-md)}:host[rounded=lg]{--mglon-button-group-border-radius: var(--mglon-schedule-radius-lg)}:host[rounded=full]{--mglon-button-group-border-radius: var(--mglon-schedule-radius-full)}:host[orientation=vertical]{flex-direction:column}:host[appereance=outline]{--mglon-button-group-background: transparent;--mglon-button-group-border-color: var(--mglon-schedule-primary-200)}:host[appereance=ghost]{--mglon-button-group-background: transparent;--mglon-button-group-border-color: transparent;--mglon-button-group-padding: 0px}:host[appereance=link]{--mglon-button-group-background: transparent;--mglon-button-group-border-color: transparent;--mglon-button-group-padding: 0px;--mglon-button-group-gap: var(--mglon-schedule-spacing-sm)}:host[density=compact]{--mglon-button-group-padding: 0px}:host[density=compact][appereance=link]{--mglon-button-group-gap: var(--mglon-schedule-spacing-xs)}:host[disabled]{opacity:var(--mglon-button-group-disabled-opacity, .5);cursor:not-allowed;pointer-events:none}:host[disabled] .selection-indicator{pointer-events:none}:host[disabled] button,:host[disabled] a{pointer-events:none}:host .selection-indicator{position:absolute;top:0;left:0;background-color:var(--mglon-button-group-indicator-background, var(--mglon-schedule-primary));border-radius:calc(var(--mglon-button-group-border-radius, var(--mglon-schedule-radius-full)) - var(--mglon-button-group-padding, var(--mglon-schedule-padding-xs)));z-index:0;transition:all var(--mglon-button-group-indicator-transition-duration, var(--mglon-schedule-transition-duration)) cubic-bezier(.16,1,.3,1)}:host ::ng-deep>button[mglon-button],:host ::ng-deep>button[mglon-icon-button],:host ::ng-deep>a[mglon-button],:host ::ng-deep>a[mglon-icon-button]{background-color:transparent!important;border:none!important;margin:0!important;position:relative;z-index:1;border-radius:var(--mglon-button-group-border-radius, var(--mglon-schedule-radius-full))!important;color:var(--mglon-button-group-button-color, var(--mglon-schedule-on-surface-1));transition:color var(--mglon-button-group-transition-duration, var(--mglon-schedule-transition-duration)) ease}:host ::ng-deep>button[mglon-button][active],:host ::ng-deep>button[mglon-icon-button][active],:host ::ng-deep>a[mglon-button][active],:host ::ng-deep>a[mglon-icon-button][active]{color:var(--mglon-button-group-button-active-color, var(--mglon-schedule-on-primary))!important;font-weight:var(--mglon-button-group-button-active-font-weight, 500)}:host ::ng-deep>button[mglon-button][disabled],:host ::ng-deep>button[mglon-icon-button][disabled],:host ::ng-deep>a[mglon-button][disabled],:host ::ng-deep>a[mglon-icon-button][disabled]{opacity:var(--mglon-button-group-disabled-opacity, .5);cursor:not-allowed;pointer-events:none}\n"] }]
1629
+ }], ctorParameters: () => [], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], appereance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appereance", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], buttons: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ButtonComponent), { isSignal: true }] }], iconButtons: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => IconButtonComponent), { isSignal: true }] }] } });
1630
+
1631
+ class HeaderSchedule {
1632
+ platformId = inject(PLATFORM_ID);
1633
+ elementRef = inject(ElementRef);
1634
+ resizeObserver;
1635
+ calendarStore = inject(CalendarStore);
1636
+ // Header UI Configuration from Store
1637
+ buttonGroupAppearance = computed(() => this.calendarStore.uiConfig().header.buttonGroup.appearance, ...(ngDevMode ? [{ debugName: "buttonGroupAppearance" }] : []));
1638
+ buttonGroupRounded = computed(() => this.calendarStore.uiConfig().header.buttonGroup.rounded, ...(ngDevMode ? [{ debugName: "buttonGroupRounded" }] : []));
1639
+ buttonGroupDensity = computed(() => this.calendarStore.uiConfig().header.buttonGroup.density, ...(ngDevMode ? [{ debugName: "buttonGroupDensity" }] : []));
1640
+ iconButtonRounded = computed(() => this.calendarStore.uiConfig().header.iconButtons.rounded, ...(ngDevMode ? [{ debugName: "iconButtonRounded" }] : []));
1641
+ todayButtonRounded = computed(() => this.calendarStore.uiConfig().header.todayButton.rounded, ...(ngDevMode ? [{ debugName: "todayButtonRounded" }] : []));
1642
+ todayButtonAppearance = computed(() => this.calendarStore.uiConfig().header.todayButton.appearance, ...(ngDevMode ? [{ debugName: "todayButtonAppearance" }] : []));
1643
+ // Inputs
1644
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
1645
+ activeView = input.required(...(ngDevMode ? [{ debugName: "activeView" }] : []));
1646
+ views = input(['month', 'week', 'day', 'resource'], ...(ngDevMode ? [{ debugName: "views" }] : []));
1647
+ editable = input(true, ...(ngDevMode ? [{ debugName: "editable" }] : []));
1648
+ // Outputs
1649
+ next = output();
1650
+ prev = output();
1651
+ today = output();
1652
+ viewChange = output();
1653
+ add = output();
1654
+ viewIcons = {
1655
+ 'month': 'calendar-month',
1656
+ 'week': 'calendar-week',
1657
+ 'day': 'calendar-day',
1658
+ 'resource': 'calendar-week', // Fallback
1659
+ 'list': 'calendar-month' // Fallback
1660
+ };
1661
+ // Responsive signal to track container size
1662
+ isSmallScreen = signal(false, ...(ngDevMode ? [{ debugName: "isSmallScreen" }] : []));
1663
+ constructor() {
1664
+ if (isPlatformBrowser(this.platformId)) {
1665
+ // Use ResizeObserver to watch the component's own width
1666
+ this.resizeObserver = new ResizeObserver((entries) => {
1667
+ for (const entry of entries) {
1668
+ const width = entry.contentRect.width;
1669
+ // Match the 'sm' breakpoint (767.98px)
1670
+ this.isSmallScreen.set(width <= 767.98);
1671
+ }
1672
+ });
1673
+ // Start observing the host element
1674
+ this.resizeObserver.observe(this.elementRef.nativeElement);
1675
+ }
1676
+ }
1677
+ ngOnDestroy() {
1678
+ // Clean up the observer
1679
+ this.resizeObserver?.disconnect();
1680
+ }
1681
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: HeaderSchedule, deps: [], target: i0.ɵɵFactoryTarget.Component });
1682
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: HeaderSchedule, isStandalone: true, selector: "mglon-header-schedule", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, activeView: { classPropertyName: "activeView", publicName: "activeView", isSignal: true, isRequired: true, transformFunction: null }, views: { classPropertyName: "views", publicName: "views", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { next: "next", prev: "prev", today: "today", viewChange: "viewChange", add: "add" }, ngImport: i0, template: "<div class=\"mglon-header\">\n\n <!-- Row 2: Navigation + Title -->\n <div class=\"mglon-header__nav\">\n <div class=\"mglon-header__nav__actions\">\n <button mglon-icon-button [rounded]=\"iconButtonRounded()\" (click)=\"prev.emit()\">\n <mglon-icon name=\"chevron-left\"></mglon-icon>\n </button>\n <button mglon-button [appereance]=\"todayButtonAppearance()\" [rounded]=\"todayButtonRounded()\"\n (click)=\"today.emit()\">\n Today\n </button>\n <button mglon-icon-button [rounded]=\"iconButtonRounded()\" (click)=\"next.emit()\">\n <mglon-icon name=\"chevron-right\"></mglon-icon>\n </button>\n </div>\n\n <h2 class=\"mglon-header__title\">{{ title() }}</h2>\n </div>\n\n <div class=\"mglon-header__actions\">\n @if (isSmallScreen()) {\n <!-- Icon buttons for small screens -->\n <mglon-button-group type=\"icon\" [disabled]=\"false\" [appereance]=\"buttonGroupAppearance()\"\n [rounded]=\"buttonGroupRounded()\" [density]=\"buttonGroupDensity()\">\n @for (view of views(); track view) {\n <button mglon-icon-button appereance=\"ghost\" [active]=\"activeView() === view\" (click)=\"viewChange.emit(view)\">\n <mglon-icon [name]=\"viewIcons[view]\" size=\"20px\"></mglon-icon>\n </button>\n }\n </mglon-button-group>\n } @else {\n <!-- Text buttons for larger screens -->\n <mglon-button-group type=\"text\" [disabled]=\"false\" [appereance]=\"buttonGroupAppearance()\"\n [rounded]=\"buttonGroupRounded()\" [density]=\"buttonGroupDensity()\">\n @for (view of views(); track view) {\n <button mglon-button appereance=\"ghost\" [active]=\"activeView() === view\" (click)=\"viewChange.emit(view)\">\n {{ view }}\n </button>\n }\n </mglon-button-group>\n }\n\n @if (editable()) {\n <button mglon-icon-button [rounded]=\"iconButtonRounded()\" (click)=\"add.emit()\">\n <mglon-icon name=\"add\"></mglon-icon>\n </button>\n }\n </div>\n\n</div>", styles: [":host{display:block;--header-bg: var(--mglon-header-bg, var(--mglon-schedule-surface));--header-padding: var(--mglon-header-padding, var(--mglon-schedule-spacing-md));--header-gap: var(--mglon-header-gap, var(--mglon-schedule-spacing-md));--header-title-size: var(--mglon-header-title-size, 1.5rem);--header-title-weight: var(--mglon-header-title-weight, 500);--header-title-color: var(--mglon-header-title-color, var(--mglon-schedule-text-primary));--header-title-mobile-size: var(--mglon-header-title-mobile-size, 1.25rem);--header-nav-gap: var(--mglon-header-nav-gap, var(--mglon-schedule-spacing-sm));background-color:var(--header-bg);container-type:inline-size;container-name:header}.mglon-header{display:flex;align-items:center;gap:var(--header-gap);padding:var(--header-padding)}@container (max-width: 767.98px){.mglon-header{flex-direction:column;align-items:stretch}}.mglon-header__nav{display:flex;align-items:center;flex-grow:1;gap:var(--header-nav-gap);justify-self:start}@container (max-width: 767.98px){.mglon-header__nav{justify-content:space-between;flex-grow:0;width:100%}}.mglon-header__nav__actions{display:flex;align-items:center;gap:var(--header-nav-gap);justify-self:start;flex-grow:0}@container (max-width: 767.98px){.mglon-header__nav__actions{justify-content:space-between}}.mglon-header__title{margin:0;font-family:var(--mglon-schedule-font-family);font-size:var(--header-title-size);font-weight:var(--header-title-weight);color:var(--header-title-color);justify-self:center;text-align:center}@container (max-width: 767.98px){.mglon-header__title{order:-1;font-size:var(--header-title-mobile-size)}}.mglon-header__actions{display:flex;gap:var(--header-gap);align-items:center;justify-self:end}@container (max-width: 767.98px){.mglon-header__actions{justify-content:space-between;width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IconComponent, selector: "mglon-icon", inputs: ["name"] }, { kind: "component", type: ButtonGroupComponent, selector: "mglon-button-group", inputs: ["type", "density", "appereance", "rounded", "orientation", "disabled"] }, { kind: "component", type: ButtonComponent, selector: "button[mglon-button], a[mglon-button]", inputs: ["appereance", "color", "size", "rounded", "density", "active", "disabled"] }, { kind: "component", type: IconButtonComponent, selector: "button[mglon-icon-button], a[mglon-icon-button]", inputs: ["appereance", "color", "size", "rounded", "active", "disabled"] }] });
1683
+ }
1684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: HeaderSchedule, decorators: [{
1685
+ type: Component,
1686
+ args: [{ selector: 'mglon-header-schedule', standalone: true, imports: [CommonModule, IconComponent, ButtonGroupComponent, ButtonComponent, IconButtonComponent], template: "<div class=\"mglon-header\">\n\n <!-- Row 2: Navigation + Title -->\n <div class=\"mglon-header__nav\">\n <div class=\"mglon-header__nav__actions\">\n <button mglon-icon-button [rounded]=\"iconButtonRounded()\" (click)=\"prev.emit()\">\n <mglon-icon name=\"chevron-left\"></mglon-icon>\n </button>\n <button mglon-button [appereance]=\"todayButtonAppearance()\" [rounded]=\"todayButtonRounded()\"\n (click)=\"today.emit()\">\n Today\n </button>\n <button mglon-icon-button [rounded]=\"iconButtonRounded()\" (click)=\"next.emit()\">\n <mglon-icon name=\"chevron-right\"></mglon-icon>\n </button>\n </div>\n\n <h2 class=\"mglon-header__title\">{{ title() }}</h2>\n </div>\n\n <div class=\"mglon-header__actions\">\n @if (isSmallScreen()) {\n <!-- Icon buttons for small screens -->\n <mglon-button-group type=\"icon\" [disabled]=\"false\" [appereance]=\"buttonGroupAppearance()\"\n [rounded]=\"buttonGroupRounded()\" [density]=\"buttonGroupDensity()\">\n @for (view of views(); track view) {\n <button mglon-icon-button appereance=\"ghost\" [active]=\"activeView() === view\" (click)=\"viewChange.emit(view)\">\n <mglon-icon [name]=\"viewIcons[view]\" size=\"20px\"></mglon-icon>\n </button>\n }\n </mglon-button-group>\n } @else {\n <!-- Text buttons for larger screens -->\n <mglon-button-group type=\"text\" [disabled]=\"false\" [appereance]=\"buttonGroupAppearance()\"\n [rounded]=\"buttonGroupRounded()\" [density]=\"buttonGroupDensity()\">\n @for (view of views(); track view) {\n <button mglon-button appereance=\"ghost\" [active]=\"activeView() === view\" (click)=\"viewChange.emit(view)\">\n {{ view }}\n </button>\n }\n </mglon-button-group>\n }\n\n @if (editable()) {\n <button mglon-icon-button [rounded]=\"iconButtonRounded()\" (click)=\"add.emit()\">\n <mglon-icon name=\"add\"></mglon-icon>\n </button>\n }\n </div>\n\n</div>", styles: [":host{display:block;--header-bg: var(--mglon-header-bg, var(--mglon-schedule-surface));--header-padding: var(--mglon-header-padding, var(--mglon-schedule-spacing-md));--header-gap: var(--mglon-header-gap, var(--mglon-schedule-spacing-md));--header-title-size: var(--mglon-header-title-size, 1.5rem);--header-title-weight: var(--mglon-header-title-weight, 500);--header-title-color: var(--mglon-header-title-color, var(--mglon-schedule-text-primary));--header-title-mobile-size: var(--mglon-header-title-mobile-size, 1.25rem);--header-nav-gap: var(--mglon-header-nav-gap, var(--mglon-schedule-spacing-sm));background-color:var(--header-bg);container-type:inline-size;container-name:header}.mglon-header{display:flex;align-items:center;gap:var(--header-gap);padding:var(--header-padding)}@container (max-width: 767.98px){.mglon-header{flex-direction:column;align-items:stretch}}.mglon-header__nav{display:flex;align-items:center;flex-grow:1;gap:var(--header-nav-gap);justify-self:start}@container (max-width: 767.98px){.mglon-header__nav{justify-content:space-between;flex-grow:0;width:100%}}.mglon-header__nav__actions{display:flex;align-items:center;gap:var(--header-nav-gap);justify-self:start;flex-grow:0}@container (max-width: 767.98px){.mglon-header__nav__actions{justify-content:space-between}}.mglon-header__title{margin:0;font-family:var(--mglon-schedule-font-family);font-size:var(--header-title-size);font-weight:var(--header-title-weight);color:var(--header-title-color);justify-self:center;text-align:center}@container (max-width: 767.98px){.mglon-header__title{order:-1;font-size:var(--header-title-mobile-size)}}.mglon-header__actions{display:flex;gap:var(--header-gap);align-items:center;justify-self:end}@container (max-width: 767.98px){.mglon-header__actions{justify-content:space-between;width:100%}}\n"] }]
1687
+ }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], activeView: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeView", required: true }] }], views: [{ type: i0.Input, args: [{ isSignal: true, alias: "views", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], next: [{ type: i0.Output, args: ["next"] }], prev: [{ type: i0.Output, args: ["prev"] }], today: [{ type: i0.Output, args: ["today"] }], viewChange: [{ type: i0.Output, args: ["viewChange"] }], add: [{ type: i0.Output, args: ["add"] }] } });
1688
+
1689
+ class MonthHeader {
1690
+ daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
1691
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
1692
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: MonthHeader, isStandalone: true, selector: "mglon-month-header", ngImport: i0, template: "<div class=\"mglon-month-header-container\">\n <!-- Spacer to align with toggle column -->\n <div class=\"mglon-month-header__spacer\"></div>\n\n <div class=\"mglon-month-header\">\n @for (day of daysOfWeek; track $index) {\n <div>{{ day }}</div>\n }\n </div>\n</div>", styles: [":host{display:block;width:100%;--month-header-bg: var(--mglon-month-header-bg, var(--mglon-schedule-surface));--month-header-border: var(--mglon-month-header-border, 1px solid var(--mglon-schedule-border));--month-header-padding: var(--mglon-month-header-padding, var(--mglon-schedule-padding-md));--month-header-text-color: var(--mglon-month-header-text-color, var(--mglon-schedule-text-primary));--month-header-font-size: var(--mglon-month-header-font-size, var(--mglon-schedule-font-size-md));--month-header-font-weight: var(--mglon-month-header-font-weight, 500);--month-header-z-index: var(--mglon-month-header-z-index, var(--mglon-schedule-z-sticky))}.mglon-month-header-container{display:flex;width:100%;position:sticky;top:0;z-index:var(--month-header-z-index);background-color:var(--month-header-bg)}.mglon-month-header__spacer{width:32px;min-width:32px;background-color:var(--month-header-bg)}.mglon-month-header{flex:1;display:grid;grid-template-columns:repeat(7,1fr);gap:0;border-bottom:var(--month-header-border)}.mglon-month-header>div{padding:var(--month-header-padding);text-align:center;font-weight:var(--month-header-font-weight);font-size:var(--month-header-font-size);color:var(--month-header-text-color)}.mglon-month-header>div:last-child{border-right:none}\n"] });
1693
+ }
1694
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthHeader, decorators: [{
1695
+ type: Component,
1696
+ args: [{ selector: 'mglon-month-header', imports: [], template: "<div class=\"mglon-month-header-container\">\n <!-- Spacer to align with toggle column -->\n <div class=\"mglon-month-header__spacer\"></div>\n\n <div class=\"mglon-month-header\">\n @for (day of daysOfWeek; track $index) {\n <div>{{ day }}</div>\n }\n </div>\n</div>", styles: [":host{display:block;width:100%;--month-header-bg: var(--mglon-month-header-bg, var(--mglon-schedule-surface));--month-header-border: var(--mglon-month-header-border, 1px solid var(--mglon-schedule-border));--month-header-padding: var(--mglon-month-header-padding, var(--mglon-schedule-padding-md));--month-header-text-color: var(--mglon-month-header-text-color, var(--mglon-schedule-text-primary));--month-header-font-size: var(--mglon-month-header-font-size, var(--mglon-schedule-font-size-md));--month-header-font-weight: var(--mglon-month-header-font-weight, 500);--month-header-z-index: var(--mglon-month-header-z-index, var(--mglon-schedule-z-sticky))}.mglon-month-header-container{display:flex;width:100%;position:sticky;top:0;z-index:var(--month-header-z-index);background-color:var(--month-header-bg)}.mglon-month-header__spacer{width:32px;min-width:32px;background-color:var(--month-header-bg)}.mglon-month-header{flex:1;display:grid;grid-template-columns:repeat(7,1fr);gap:0;border-bottom:var(--month-header-border)}.mglon-month-header>div{padding:var(--month-header-padding);text-align:center;font-weight:var(--month-header-font-weight);font-size:var(--month-header-font-size);color:var(--month-header-text-color)}.mglon-month-header>div:last-child{border-right:none}\n"] }]
1697
+ }] });
1698
+
1699
+ class MonthDayIndicator {
1700
+ day = input.required(...(ngDevMode ? [{ debugName: "day" }] : []));
1701
+ isToday = computed(() => this.day().date.toDateString() === new Date().toDateString(), ...(ngDevMode ? [{ debugName: "isToday" }] : []));
1702
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthDayIndicator, deps: [], target: i0.ɵɵFactoryTarget.Component });
1703
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: MonthDayIndicator, isStandalone: true, selector: "mglon-month-day-indicator", inputs: { day: { classPropertyName: "day", publicName: "day", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"mglon-month-day-indicator__container\">\n <span [class.today]=\"isToday()\">{{ day().day }}</span>\n</div>", styles: [".mglon-month-day-indicator__container{font-size:var(--month-cell-day-number-size);font-weight:var(--month-cell-day-number-weight);line-height:1}.mglon-month-day-indicator__container span{color:var(--mglon-month-day-indicator-text-color, var(--mglon-schedule-text-secondary));padding:var(--mglon-month-day-indicator-padding, var(--mglon-schedule-padding-xs));border-radius:var(--mglon-month-day-indicator-border-radius, var(--mglon-schedule-radius-full))}.mglon-month-day-indicator__container .today{color:var(--mglon-month-day-indicator-today-background-color, var(--mglon-schedule-error));font-weight:600}\n"] });
1704
+ }
1705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthDayIndicator, decorators: [{
1706
+ type: Component,
1707
+ args: [{ selector: 'mglon-month-day-indicator', imports: [], template: "<div class=\"mglon-month-day-indicator__container\">\n <span [class.today]=\"isToday()\">{{ day().day }}</span>\n</div>", styles: [".mglon-month-day-indicator__container{font-size:var(--month-cell-day-number-size);font-weight:var(--month-cell-day-number-weight);line-height:1}.mglon-month-day-indicator__container span{color:var(--mglon-month-day-indicator-text-color, var(--mglon-schedule-text-secondary));padding:var(--mglon-month-day-indicator-padding, var(--mglon-schedule-padding-xs));border-radius:var(--mglon-month-day-indicator-border-radius, var(--mglon-schedule-radius-full))}.mglon-month-day-indicator__container .today{color:var(--mglon-month-day-indicator-today-background-color, var(--mglon-schedule-error));font-weight:600}\n"] }]
1708
+ }], propDecorators: { day: [{ type: i0.Input, args: [{ isSignal: true, alias: "day", required: true }] }] } });
1709
+
1710
+ class MonthCell {
1711
+ store = inject(CalendarStore);
1712
+ // Input for the day data
1713
+ day = input.required(...(ngDevMode ? [{ debugName: "day" }] : []));
1714
+ /** Whether a slot is currently being dragged or resized over this cell */
1715
+ isDragOver = computed(() => {
1716
+ const dragState = this.store.dragState();
1717
+ const resizeState = this.store.resizeState();
1718
+ const hoverDate = dragState.hoverDate || resizeState.hoverDate;
1719
+ if (!hoverDate)
1720
+ return false;
1721
+ return hoverDate.getTime() === this.day().date.getTime();
1722
+ }, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
1723
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthCell, deps: [], target: i0.ɵɵFactoryTarget.Component });
1724
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: MonthCell, isStandalone: true, selector: "mglon-month-cell", inputs: { day: { classPropertyName: "day", publicName: "day", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"mglon-month-cell\" [class.mglon-month-cell--other-month]=\"!day().isCurrentMonth\"\n [class.mglon-month-cell--current-month]=\"day().isCurrentMonth\" [class.mglon-month-cell--drag-over]=\"isDragOver()\"\n [attr.data-mglon-date]=\"day().date.getTime()\">\n <mglon-month-day-indicator [day]=\"day()\"></mglon-month-day-indicator>\n</div>", styles: [":host{display:block;height:100%;width:100%;--month-cell-padding: var(--mglon-month-cell-padding, var(--mglon-schedule-padding-sm));--month-cell-bg: var(--mglon-month-cell-bg, var(--mglon-schedule-surface));--month-cell-hover-bg: var(--mglon-month-cell-hover-bg, var(--mglon-schedule-surface-1));--month-cell-text-primary: var(--mglon-month-cell-text-primary, var(--mglon-schedule-text-primary));--month-cell-text-secondary: var(--mglon-month-cell-text-secondary, var(--mglon-schedule-text-secondary));--month-cell-day-number-size: var(--mglon-month-cell-day-number-size, var(--mglon-schedule-font-size-sm));--month-cell-day-number-weight: var(--mglon-month-cell-day-number-weight, 500);--month-cell-transition-duration: var(--mglon-month-cell-transition-duration, var(--mglon-schedule-transition-duration))}.mglon-month-cell{position:relative;padding:var(--month-cell-padding);background-color:var(--month-cell-bg);overflow:hidden;height:100%;box-sizing:border-box;transition-duration:var(--month-cell-transition-duration)}.mglon-month-cell:last-child{border-right:none}.mglon-month-cell:hover,.mglon-month-cell--drag-over{background-color:var(--month-cell-hover-bg)}.mglon-month-cell--drag-over{box-shadow:inset 0 0 0 2px var(--mglon-schedule-primary);z-index:1}.mglon-month-cell--other-month .mglon-month-cell__day-number{color:var(--month-cell-text-secondary);opacity:.5}.mglon-month-cell--current-month .mglon-month-cell__day-number{color:var(--month-cell-text-primary)}\n"], dependencies: [{ kind: "component", type: MonthDayIndicator, selector: "mglon-month-day-indicator", inputs: ["day"] }] });
1725
+ }
1726
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthCell, decorators: [{
1727
+ type: Component,
1728
+ args: [{ selector: 'mglon-month-cell', standalone: true, imports: [MonthDayIndicator], template: "<div class=\"mglon-month-cell\" [class.mglon-month-cell--other-month]=\"!day().isCurrentMonth\"\n [class.mglon-month-cell--current-month]=\"day().isCurrentMonth\" [class.mglon-month-cell--drag-over]=\"isDragOver()\"\n [attr.data-mglon-date]=\"day().date.getTime()\">\n <mglon-month-day-indicator [day]=\"day()\"></mglon-month-day-indicator>\n</div>", styles: [":host{display:block;height:100%;width:100%;--month-cell-padding: var(--mglon-month-cell-padding, var(--mglon-schedule-padding-sm));--month-cell-bg: var(--mglon-month-cell-bg, var(--mglon-schedule-surface));--month-cell-hover-bg: var(--mglon-month-cell-hover-bg, var(--mglon-schedule-surface-1));--month-cell-text-primary: var(--mglon-month-cell-text-primary, var(--mglon-schedule-text-primary));--month-cell-text-secondary: var(--mglon-month-cell-text-secondary, var(--mglon-schedule-text-secondary));--month-cell-day-number-size: var(--mglon-month-cell-day-number-size, var(--mglon-schedule-font-size-sm));--month-cell-day-number-weight: var(--mglon-month-cell-day-number-weight, 500);--month-cell-transition-duration: var(--mglon-month-cell-transition-duration, var(--mglon-schedule-transition-duration))}.mglon-month-cell{position:relative;padding:var(--month-cell-padding);background-color:var(--month-cell-bg);overflow:hidden;height:100%;box-sizing:border-box;transition-duration:var(--month-cell-transition-duration)}.mglon-month-cell:last-child{border-right:none}.mglon-month-cell:hover,.mglon-month-cell--drag-over{background-color:var(--month-cell-hover-bg)}.mglon-month-cell--drag-over{box-shadow:inset 0 0 0 2px var(--mglon-schedule-primary);z-index:1}.mglon-month-cell--other-month .mglon-month-cell__day-number{color:var(--month-cell-text-secondary);opacity:.5}.mglon-month-cell--current-month .mglon-month-cell__day-number{color:var(--month-cell-text-primary)}\n"] }]
1729
+ }], propDecorators: { day: [{ type: i0.Input, args: [{ isSignal: true, alias: "day", required: true }] }] } });
1730
+
1731
+ class ZigzagDirective {
1732
+ /**
1733
+ * Side(s) to apply the zigzag effect to.
1734
+ * Can be a single side string or an array of sides.
1735
+ */
1736
+ sides = input.required({ ...(ngDevMode ? { debugName: "sides" } : {}), alias: 'mglonZigzag' });
1737
+ /**
1738
+ * Size of the zigzag teeth (e.g., '10px', '0.5em').
1739
+ * Defaults to '5px' if not specified.
1740
+ */
1741
+ zigzagSize = input('5px', ...(ngDevMode ? [{ debugName: "zigzagSize" }] : []));
1742
+ elementRef = inject(ElementRef);
1743
+ renderer = inject(Renderer2);
1744
+ constructor() {
1745
+ effect(() => {
1746
+ const sidesInput = this.sides();
1747
+ const sides = Array.isArray(sidesInput) ? sidesInput : [sidesInput];
1748
+ // Clear all existing zigzag classes first
1749
+ const allSides = ['top', 'right', 'bottom', 'left'];
1750
+ allSides.forEach(side => {
1751
+ this.renderer.removeClass(this.elementRef.nativeElement, `mglon-zigzag-${side}`);
1752
+ });
1753
+ // Apply classes for requested sides
1754
+ sides.forEach(side => {
1755
+ if (side && allSides.includes(side)) {
1756
+ this.renderer.addClass(this.elementRef.nativeElement, `mglon-zigzag-${side}`);
1757
+ }
1758
+ });
1759
+ });
1760
+ }
1761
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZigzagDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1762
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: ZigzagDirective, isStandalone: true, selector: "[mglonZigzag]", inputs: { sides: { classPropertyName: "sides", publicName: "mglonZigzag", isSignal: true, isRequired: true, transformFunction: null }, zigzagSize: { classPropertyName: "zigzagSize", publicName: "zigzagSize", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.--mglon-zigzag-size": "zigzagSize()" } }, ngImport: i0 });
1763
+ }
1764
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZigzagDirective, decorators: [{
1765
+ type: Directive,
1766
+ args: [{
1767
+ selector: '[mglonZigzag]',
1768
+ standalone: true,
1769
+ host: {
1770
+ '[style.--mglon-zigzag-size]': 'zigzagSize()'
1771
+ }
1772
+ }]
1773
+ }], ctorParameters: () => [], propDecorators: { sides: [{ type: i0.Input, args: [{ isSignal: true, alias: "mglonZigzag", required: true }] }], zigzagSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "zigzagSize", required: false }] }] } });
1774
+
1775
+ class ResizableDirective {
1776
+ /**
1777
+ * Sides to allow resizing on.
1778
+ */
1779
+ sides = input.required({ ...(ngDevMode ? { debugName: "sides" } : {}), alias: 'mglonResizable' });
1780
+ /**
1781
+ * Size of the resize handle area in pixels.
1782
+ */
1783
+ handleSize = input(6, ...(ngDevMode ? [{ debugName: "handleSize" }] : []));
1784
+ /**
1785
+ * Emitted when a resize interaction starts (mousedown on handle).
1786
+ */
1787
+ resizeStart = output();
1788
+ elementRef = inject(ElementRef);
1789
+ renderer = inject(Renderer2);
1790
+ store = inject(CalendarStore);
1791
+ destroyMouseDownListener;
1792
+ constructor() {
1793
+ effect(() => {
1794
+ const sidesInput = this.sides();
1795
+ const sides = Array.isArray(sidesInput) ? sidesInput : [sidesInput];
1796
+ const allSides = ['top', 'right', 'bottom', 'left'];
1797
+ allSides.forEach(side => {
1798
+ this.renderer.removeClass(this.elementRef.nativeElement, `mglon-resizable-${side}`);
1799
+ });
1800
+ sides.forEach(side => {
1801
+ if (side && allSides.includes(side)) {
1802
+ this.renderer.addClass(this.elementRef.nativeElement, `mglon-resizable-${side}`);
1803
+ }
1804
+ });
1805
+ });
1806
+ }
1807
+ onMouseEnter() {
1808
+ if (this.destroyMouseDownListener) {
1809
+ return;
1810
+ }
1811
+ this.destroyMouseDownListener = this.renderer.listen(this.elementRef.nativeElement, 'mousedown', (event) => this.handleMouseDown(event));
1812
+ }
1813
+ onMouseLeave() {
1814
+ if (this.destroyMouseDownListener) {
1815
+ this.destroyMouseDownListener();
1816
+ this.destroyMouseDownListener = undefined;
1817
+ }
1818
+ }
1819
+ ngOnDestroy() {
1820
+ this.cleanupListeners();
1821
+ }
1822
+ cleanupListeners() {
1823
+ if (this.destroyMouseDownListener) {
1824
+ this.destroyMouseDownListener();
1825
+ this.destroyMouseDownListener = undefined;
1826
+ }
1827
+ }
1828
+ handleMouseDown(event) {
1829
+ const sidesInput = this.sides();
1830
+ const activeSides = Array.isArray(sidesInput) ? sidesInput : [sidesInput];
1831
+ const rect = this.elementRef.nativeElement.getBoundingClientRect();
1832
+ const x = event.clientX - rect.left;
1833
+ const y = event.clientY - rect.top;
1834
+ const w = rect.width;
1835
+ const h = rect.height;
1836
+ const handle = this.handleSize();
1837
+ let clickedSide = null;
1838
+ if (activeSides.includes('left') && x <= handle) {
1839
+ clickedSide = 'left';
1840
+ }
1841
+ else if (activeSides.includes('right') && x >= w - handle) {
1842
+ clickedSide = 'right';
1843
+ }
1844
+ else if (activeSides.includes('top') && y <= handle) {
1845
+ clickedSide = 'top';
1846
+ }
1847
+ else if (activeSides.includes('bottom') && y >= h - handle) {
1848
+ clickedSide = 'bottom';
1849
+ }
1850
+ if (clickedSide) {
1851
+ if (this.store.interactionMode() !== 'none' && this.store.interactionMode() !== 'dragging') {
1852
+ // Only allow resizing if nothing else is happening
1853
+ // Note: we might be in 'dragging' mode because of pointerdown already firing on MonthSlot
1854
+ // So we might need to override it.
1855
+ return;
1856
+ }
1857
+ event.preventDefault();
1858
+ event.stopPropagation();
1859
+ this.store.setInteractionMode('resizing');
1860
+ this.resizeStart.emit({ side: clickedSide, event });
1861
+ // Add global mouseup to reset interaction mode
1862
+ const onUp = () => {
1863
+ this.store.setInteractionMode('none');
1864
+ window.removeEventListener('mouseup', onUp);
1865
+ };
1866
+ window.addEventListener('mouseup', onUp);
1867
+ }
1868
+ }
1869
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResizableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1870
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: ResizableDirective, isStandalone: true, selector: "[mglonResizable]", inputs: { sides: { classPropertyName: "sides", publicName: "mglonResizable", isSignal: true, isRequired: true, transformFunction: null }, handleSize: { classPropertyName: "handleSize", publicName: "handleSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { resizeStart: "resizeStart" }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()" }, properties: { "style.--mglon-resize-handle-size": "handleSize() + \"px\"" } }, ngImport: i0 });
1871
+ }
1872
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResizableDirective, decorators: [{
1873
+ type: Directive,
1874
+ args: [{
1875
+ selector: '[mglonResizable]',
1876
+ standalone: true,
1877
+ host: {
1878
+ '[style.--mglon-resize-handle-size]': 'handleSize() + "px"'
1879
+ }
1880
+ }]
1881
+ }], ctorParameters: () => [], propDecorators: { sides: [{ type: i0.Input, args: [{ isSignal: true, alias: "mglonResizable", required: true }] }], handleSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "handleSize", required: false }] }], resizeStart: [{ type: i0.Output, args: ["resizeStart"] }], onMouseEnter: [{
1882
+ type: HostListener,
1883
+ args: ['mouseenter']
1884
+ }], onMouseLeave: [{
1885
+ type: HostListener,
1886
+ args: ['mouseleave']
1887
+ }] } });
1888
+
1889
+ class MonthRecurrenceDirective {
1890
+ el = inject(ElementRef);
1891
+ vcr = inject(ViewContainerRef);
1892
+ /** Whether the event is recurrent */
1893
+ isRecurrent = input(false, { ...(ngDevMode ? { debugName: "isRecurrent" } : {}), alias: 'mglonMonthRecurrence' });
1894
+ constructor() {
1895
+ effect(() => {
1896
+ if (this.isRecurrent()) {
1897
+ this.addIcon();
1898
+ }
1899
+ else {
1900
+ this.removeIcon();
1901
+ }
1902
+ });
1903
+ }
1904
+ addIcon() {
1905
+ // Check if icon already exists to avoid duplicates
1906
+ if (this.el.nativeElement.querySelector('.mglon-month-slot__recurrence-icon')) {
1907
+ return;
1908
+ }
1909
+ const componentRef = this.vcr.createComponent(IconComponent);
1910
+ componentRef.setInput('name', 'cycle');
1911
+ // Get the DOM element of the icon
1912
+ const iconElement = componentRef.location.nativeElement;
1913
+ iconElement.classList.add('mglon-month-slot__recurrence-icon');
1914
+ // Insert before the text content
1915
+ this.el.nativeElement.prepend(iconElement);
1916
+ }
1917
+ removeIcon() {
1918
+ const icon = this.el.nativeElement.querySelector('.mglon-month-slot__recurrence-icon');
1919
+ if (icon) {
1920
+ icon.remove();
1921
+ }
1922
+ }
1923
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthRecurrenceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1924
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: MonthRecurrenceDirective, isStandalone: true, selector: "[mglonMonthRecurrence]", inputs: { isRecurrent: { classPropertyName: "isRecurrent", publicName: "mglonMonthRecurrence", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
1925
+ }
1926
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthRecurrenceDirective, decorators: [{
1927
+ type: Directive,
1928
+ args: [{
1929
+ selector: '[mglonMonthRecurrence]',
1930
+ standalone: true
1931
+ }]
1932
+ }], ctorParameters: () => [], propDecorators: { isRecurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "mglonMonthRecurrence", required: false }] }] } });
1933
+
1934
+ class AllDayDirective {
1935
+ el = inject(ElementRef);
1936
+ vcr = inject(ViewContainerRef);
1937
+ /** Whether the event is all-day */
1938
+ isAllDay = input(false, { ...(ngDevMode ? { debugName: "isAllDay" } : {}), alias: 'mglonAllDay' });
1939
+ /** Color for the dot icon */
1940
+ dotColor = input('', ...(ngDevMode ? [{ debugName: "dotColor" }] : []));
1941
+ constructor() {
1942
+ effect(() => {
1943
+ if (this.isAllDay()) {
1944
+ this.addIcon();
1945
+ // The host binding in MonthSlot will handle most styling,
1946
+ // but we add a class for extra specificity if needed.
1947
+ this.el.nativeElement.classList.add('mglon-event--all-day');
1948
+ }
1949
+ else {
1950
+ this.removeIcon();
1951
+ this.el.nativeElement.classList.remove('mglon-event--all-day');
1952
+ }
1953
+ });
1954
+ // Handle dynamic color changes for the dot
1955
+ effect(() => {
1956
+ const color = this.dotColor();
1957
+ const icon = this.el.nativeElement.querySelector('.mglon-event__all-day-icon');
1958
+ if (icon && color) {
1959
+ icon.style.color = color;
1960
+ }
1961
+ });
1962
+ }
1963
+ addIcon() {
1964
+ if (this.el.nativeElement.querySelector('.mglon-event__all-day-icon')) {
1965
+ return;
1966
+ }
1967
+ const componentRef = this.vcr.createComponent(IconComponent);
1968
+ componentRef.setInput('name', 'dot');
1969
+ const iconElement = componentRef.location.nativeElement;
1970
+ iconElement.classList.add('mglon-event__all-day-icon');
1971
+ if (this.dotColor()) {
1972
+ iconElement.style.color = this.dotColor();
1973
+ }
1974
+ // Insert before the title/text
1975
+ this.el.nativeElement.prepend(iconElement);
1976
+ }
1977
+ removeIcon() {
1978
+ const icon = this.el.nativeElement.querySelector('.mglon-event__all-day-icon');
1979
+ if (icon) {
1980
+ icon.remove();
1981
+ }
1982
+ }
1983
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AllDayDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1984
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: AllDayDirective, isStandalone: true, selector: "[mglonAllDay]", inputs: { isAllDay: { classPropertyName: "isAllDay", publicName: "mglonAllDay", isSignal: true, isRequired: false, transformFunction: null }, dotColor: { classPropertyName: "dotColor", publicName: "dotColor", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
1985
+ }
1986
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AllDayDirective, decorators: [{
1987
+ type: Directive,
1988
+ args: [{
1989
+ selector: '[mglonAllDay]',
1990
+ standalone: true
1991
+ }]
1992
+ }], ctorParameters: () => [], propDecorators: { isAllDay: [{ type: i0.Input, args: [{ isSignal: true, alias: "mglonAllDay", required: false }] }], dotColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "dotColor", required: false }] }] } });
1993
+
1994
+ /** Maps EventSlotRadius to CSS variable names */
1995
+ const RADIUS_VAR_MAP = {
1996
+ 'none': '0',
1997
+ 'sm': 'var(--mglon-schedule-radius-sm)', // Small fixed value for subtle rounding on slots
1998
+ 'full': 'var(--mglon-schedule-radius-full)'
1999
+ };
2000
+ class MonthSlot {
2001
+ store = inject(CalendarStore);
2002
+ slot = input.required(...(ngDevMode ? [{ debugName: "slot" }] : []));
2003
+ /** Whether this specific event is being dragged */
2004
+ isDragging = computed(() => this.store.dragState().eventId === this.slot().idEvent, ...(ngDevMode ? [{ debugName: "isDragging" }] : []));
2005
+ /** Whether this event is currently hovered globally */
2006
+ isHovered = computed(() => this.store.hoveredEventId() === this.slot().idEvent, ...(ngDevMode ? [{ debugName: "isHovered" }] : []));
2007
+ /**
2008
+ * Gets the event data from the store using the slot's event ID.
2009
+ */
2010
+ event = computed(() => {
2011
+ return this.store.getEvent(this.slot().idEvent);
2012
+ }, ...(ngDevMode ? [{ debugName: "event" }] : []));
2013
+ /** Whether this event is part of a recurrence series */
2014
+ isRecurrent = computed(() => {
2015
+ const e = this.event();
2016
+ return e?.type === 'event' && e.isRecurrenceInstance === true;
2017
+ }, ...(ngDevMode ? [{ debugName: "isRecurrent" }] : []));
2018
+ /** Whether the event is all-day */
2019
+ isAllDay = computed(() => {
2020
+ return this.event()?.isAllDay === true;
2021
+ }, ...(ngDevMode ? [{ debugName: "isAllDay" }] : []));
2022
+ /**
2023
+ * Display title for the slot.
2024
+ */
2025
+ title = computed(() => {
2026
+ return this.event()?.title ?? '';
2027
+ }, ...(ngDevMode ? [{ debugName: "title" }] : []));
2028
+ /**
2029
+ * Complete color scheme calculation based on event type
2030
+ */
2031
+ colorScheme = computed(() => {
2032
+ // 1. Resolve base raw color
2033
+ const rawColor = this.rawColor();
2034
+ // 2. Generate all adaptive variants
2035
+ const scheme = generateAdaptiveColorScheme(rawColor);
2036
+ // 3. Select variant based on configuration and event type
2037
+ const useDynamic = this.store.uiConfig().grid.useDynamicColors;
2038
+ if (!useDynamic) {
2039
+ return scheme.raw;
2040
+ }
2041
+ return this.isRecurrent() ? scheme.pastel : scheme.vivid;
2042
+ }, ...(ngDevMode ? [{ debugName: "colorScheme" }] : []));
2043
+ /**
2044
+ * Raw color resolved for the event (before adaptive variants)
2045
+ */
2046
+ rawColor = computed(() => {
2047
+ return getEventColor({ color: this.slot().color, resourceId: this.event()?.resourceId }, (id) => this.store.getResource(id), this.store.uiConfig().grid.eventSlots.color || '#1a73e8');
2048
+ }, ...(ngDevMode ? [{ debugName: "rawColor" }] : []));
2049
+ /**
2050
+ * Border radius from uiConfig, mapped to CSS variable.
2051
+ */
2052
+ slotRadius = computed(() => {
2053
+ const rounded = this.store.uiConfig().grid.eventSlots.rounded;
2054
+ return RADIUS_VAR_MAP[rounded];
2055
+ }, ...(ngDevMode ? [{ debugName: "slotRadius" }] : []));
2056
+ /**
2057
+ * Determines which sides get zigzag effect based on slot type.
2058
+ * - 'first': right side (continues to next week)
2059
+ * - 'last': left side (comes from previous week)
2060
+ * - 'middle': both sides (spans entire week)
2061
+ * - 'full': no zigzag (complete within week)
2062
+ */
2063
+ zigzagSides = computed(() => {
2064
+ const type = this.slot().type;
2065
+ switch (type) {
2066
+ case 'first':
2067
+ return ['right'];
2068
+ case 'last':
2069
+ return ['left'];
2070
+ case 'middle':
2071
+ return ['left', 'right'];
2072
+ default:
2073
+ return [];
2074
+ }
2075
+ }, ...(ngDevMode ? [{ debugName: "zigzagSides" }] : []));
2076
+ /**
2077
+ * Determines which sides are resizable.
2078
+ * Only sides without zigzag are resizable, and only if resizableEvents is enabled.
2079
+ */
2080
+ resizableSides = computed(() => {
2081
+ // Check global config from store
2082
+ if (!this.store.config().resizableEvents) {
2083
+ return [];
2084
+ }
2085
+ // Check resource-specific config if available
2086
+ const event = this.event();
2087
+ if (event?.resourceId) {
2088
+ const resource = this.store.getResource(event.resourceId);
2089
+ if (resource && resource.resizableEvents === false) {
2090
+ return [];
2091
+ }
2092
+ }
2093
+ const type = this.slot().type;
2094
+ switch (type) {
2095
+ case 'first':
2096
+ return ['left'];
2097
+ case 'last':
2098
+ return ['right'];
2099
+ case 'middle':
2100
+ return [];
2101
+ case 'full':
2102
+ return ['left', 'right'];
2103
+ default:
2104
+ return [];
2105
+ }
2106
+ }, ...(ngDevMode ? [{ debugName: "resizableSides" }] : []));
2107
+ dragDelayTimer;
2108
+ DRAG_DELAY = 150; // ms
2109
+ DRAG_THRESHOLD = 5; // px
2110
+ startPointerPos = { x: 0, y: 0 };
2111
+ clickTimer;
2112
+ ignoreNextClick = false;
2113
+ onPointerDown(event) {
2114
+ // Only handle primary button and if no other interaction is active
2115
+ if (event.button !== 0 || this.store.interactionMode() !== 'none')
2116
+ return;
2117
+ // Check if we clicked a resize handle
2118
+ const target = event.currentTarget;
2119
+ const rect = target.getBoundingClientRect();
2120
+ const x = event.clientX - rect.left;
2121
+ const sides = this.resizableSides();
2122
+ const handleSize = 6; // Matching ResizableDirective default
2123
+ if (sides.includes('left') && x <= handleSize)
2124
+ return;
2125
+ if (sides.includes('right') && x >= rect.width - handleSize)
2126
+ return;
2127
+ // Stop propagation immediately to prevent background selection from seeing this event
2128
+ event.stopPropagation();
2129
+ this.startPointerPos = { x: event.clientX, y: event.clientY };
2130
+ // Lock the mutex immediately to block other potential listeners (like Selection)
2131
+ this.store.setInteractionMode('dragging');
2132
+ this.dragDelayTimer = setTimeout(() => {
2133
+ this.initiateDrag(event);
2134
+ }, this.DRAG_DELAY);
2135
+ // Bind movement and release to handle cancellation
2136
+ const onMove = (e) => this.onGlobalMove(e);
2137
+ const onUp = (e) => {
2138
+ this.onGlobalUp(e);
2139
+ window.removeEventListener('pointermove', onMove);
2140
+ window.removeEventListener('pointerup', onUp);
2141
+ window.removeEventListener('pointercancel', onUp);
2142
+ };
2143
+ window.addEventListener('pointermove', onMove);
2144
+ window.addEventListener('pointerup', onUp);
2145
+ window.addEventListener('pointercancel', onUp);
2146
+ }
2147
+ initiateDrag(event) {
2148
+ const target = event.currentTarget || event.target;
2149
+ if (target.setPointerCapture) {
2150
+ target.setPointerCapture(event.pointerId);
2151
+ }
2152
+ // Calculate grabDate (exact day clicked)
2153
+ const rect = target.getBoundingClientRect();
2154
+ const clickX = this.startPointerPos.x - rect.left;
2155
+ const slotWidth = rect.width;
2156
+ const daysSpan = differenceInCalendarDays(this.slot().end, this.slot().start) + 1;
2157
+ const dayOffset = Math.max(0, Math.min(Math.floor((clickX / slotWidth) * daysSpan), daysSpan - 1));
2158
+ const grabDate = addDays(this.slot().start, dayOffset);
2159
+ this.store.setDragStart(this.slot().idEvent, grabDate);
2160
+ this.store.setInteractionMode('dragging');
2161
+ // Dispatch dragStart interaction
2162
+ this.store.dispatchInteraction('dragStart', this.slot().idEvent, {
2163
+ event: this.event(),
2164
+ slotId: this.slot().id,
2165
+ data: {
2166
+ grabDate,
2167
+ hoverDate: null
2168
+ }
2169
+ });
2170
+ }
2171
+ onGlobalMove(event) {
2172
+ if (this.store.dragState().eventId) { // Check if we are actually dragging (timer finished)
2173
+ this.onPointerMove(event);
2174
+ }
2175
+ else if (this.dragDelayTimer) {
2176
+ // Check if we moved too much before the delay finished
2177
+ const dist = Math.sqrt(Math.pow(event.clientX - this.startPointerPos.x, 2) +
2178
+ Math.pow(event.clientY - this.startPointerPos.y, 2));
2179
+ if (dist > this.DRAG_THRESHOLD) {
2180
+ this.store.setInteractionMode('none'); // Release mutex if it was a scroll/swipe
2181
+ this.cancelDragDelay();
2182
+ }
2183
+ }
2184
+ }
2185
+ onGlobalUp(event) {
2186
+ this.cancelDragDelay();
2187
+ if (this.store.dragState().eventId) {
2188
+ this.onPointerUp(event);
2189
+ // Occlude the next click event that follows pointerup
2190
+ this.ignoreNextClick = true;
2191
+ }
2192
+ else {
2193
+ // It was just a click or a cancelled drag, release mutex
2194
+ this.store.setInteractionMode('none');
2195
+ }
2196
+ }
2197
+ cancelDragDelay() {
2198
+ if (this.dragDelayTimer) {
2199
+ clearTimeout(this.dragDelayTimer);
2200
+ this.dragDelayTimer = undefined;
2201
+ }
2202
+ }
2203
+ onPointerMove(event) {
2204
+ if (!this.isDragging())
2205
+ return;
2206
+ // Detect which cell we are over using coordinates
2207
+ const element = document.elementFromPoint(event.clientX, event.clientY);
2208
+ const cell = element?.closest('.mglon-month-cell');
2209
+ if (cell) {
2210
+ const timestamp = cell.getAttribute('data-mglon-date');
2211
+ if (timestamp) {
2212
+ const hoverDate = new Date(parseInt(timestamp, 10));
2213
+ this.store.setDragHover(hoverDate);
2214
+ this.store.updateDraggedEventPosition();
2215
+ // Dispatch drag interaction event
2216
+ this.store.dispatchInteraction('drag', this.slot().idEvent, {
2217
+ event: this.event(),
2218
+ slotId: this.slot().id,
2219
+ data: {
2220
+ grabDate: this.store.dragState().grabDate,
2221
+ hoverDate: hoverDate
2222
+ }
2223
+ });
2224
+ }
2225
+ }
2226
+ }
2227
+ onPointerUp(event) {
2228
+ // Dispatch dragEnd interaction before clearing state
2229
+ const dragState = this.store.dragState();
2230
+ this.store.dispatchInteraction('dragEnd', this.slot().idEvent, {
2231
+ event: this.event(),
2232
+ slotId: this.slot().id,
2233
+ data: {
2234
+ grabDate: dragState.grabDate,
2235
+ hoverDate: dragState.hoverDate
2236
+ }
2237
+ });
2238
+ this.store.setInteractionMode('none');
2239
+ this.store.clearDragState();
2240
+ }
2241
+ onResizeStart(event) {
2242
+ this.store.setResizeStart(this.slot().idEvent, event.side);
2243
+ // Dispatch resizeStart interaction
2244
+ this.store.dispatchInteraction('resizeStart', this.slot().idEvent, {
2245
+ event: this.event(),
2246
+ slotId: this.slot().id,
2247
+ data: {
2248
+ side: event.side,
2249
+ date: this.slot().start // Basic reference date for start
2250
+ }
2251
+ });
2252
+ // Bind movement and release for real-time resize feedback
2253
+ // Similar to drag-and-drop, we use global listeners
2254
+ const onMove = (e) => this.onGlobalResizeMove(e);
2255
+ const onUp = () => {
2256
+ // Occlude the next click event
2257
+ this.ignoreNextClick = true;
2258
+ // Dispatch resizeEnd interaction before clearing state
2259
+ const resizeState = this.store.resizeState();
2260
+ this.store.dispatchInteraction('resizeEnd', this.slot().idEvent, {
2261
+ event: this.event(),
2262
+ slotId: this.slot().id,
2263
+ data: {
2264
+ side: resizeState.side,
2265
+ date: resizeState.hoverDate || (resizeState.side === 'left' ? this.slot().start : this.slot().end)
2266
+ }
2267
+ });
2268
+ this.store.clearResizeState();
2269
+ this.store.setInteractionMode('none');
2270
+ window.removeEventListener('pointermove', onMove);
2271
+ window.removeEventListener('pointerup', onUp);
2272
+ window.removeEventListener('pointercancel', onUp);
2273
+ };
2274
+ window.addEventListener('pointermove', onMove);
2275
+ window.addEventListener('pointerup', onUp);
2276
+ window.addEventListener('pointercancel', onUp);
2277
+ }
2278
+ onGlobalResizeMove(event) {
2279
+ if (this.store.interactionMode() !== 'resizing')
2280
+ return;
2281
+ // Detect which cell we are over using coordinates
2282
+ const element = document.elementFromPoint(event.clientX, event.clientY);
2283
+ const cell = element?.closest('.mglon-month-cell');
2284
+ if (cell) {
2285
+ const timestamp = cell.getAttribute('data-mglon-date');
2286
+ if (timestamp) {
2287
+ const hoverDate = new Date(parseInt(timestamp, 10));
2288
+ this.store.setResizeHover(hoverDate);
2289
+ this.store.updateResizedEvent();
2290
+ // Dispatch resize interaction event
2291
+ this.store.dispatchInteraction('resize', this.slot().idEvent, {
2292
+ event: this.event(),
2293
+ slotId: this.slot().id,
2294
+ data: {
2295
+ side: this.store.resizeState().side,
2296
+ date: hoverDate
2297
+ }
2298
+ });
2299
+ }
2300
+ }
2301
+ }
2302
+ onMouseEnter() {
2303
+ if (this.store.interactionMode() !== 'none')
2304
+ return;
2305
+ this.store.setHoveredEvent(this.slot().idEvent);
2306
+ this.store.dispatchInteraction('mouseenter', this.slot().idEvent, {
2307
+ event: this.event(),
2308
+ slotId: this.slot().id
2309
+ });
2310
+ }
2311
+ onMouseLeave() {
2312
+ if (this.store.interactionMode() !== 'none')
2313
+ return;
2314
+ this.store.setHoveredEvent(null);
2315
+ this.store.dispatchInteraction('mouseleave', this.slot().idEvent, {
2316
+ event: this.event(),
2317
+ slotId: this.slot().id
2318
+ });
2319
+ }
2320
+ onClick(event) {
2321
+ event.stopPropagation();
2322
+ // Prevent click if we just finished a drag or resize
2323
+ if (this.ignoreNextClick) {
2324
+ this.ignoreNextClick = false;
2325
+ return;
2326
+ }
2327
+ // Double click detection: if another click arrives within 250ms, cancel this one
2328
+ if (this.clickTimer) {
2329
+ clearTimeout(this.clickTimer);
2330
+ this.clickTimer = undefined;
2331
+ return;
2332
+ }
2333
+ this.clickTimer = setTimeout(() => {
2334
+ this.clickTimer = undefined;
2335
+ this.store.dispatchInteraction('click', this.slot().idEvent, {
2336
+ event: this.event(),
2337
+ slotId: this.slot().id,
2338
+ originalEvent: event
2339
+ });
2340
+ }, 250);
2341
+ }
2342
+ onDblClick(event) {
2343
+ event.stopPropagation();
2344
+ if (this.clickTimer) {
2345
+ clearTimeout(this.clickTimer);
2346
+ this.clickTimer = undefined;
2347
+ }
2348
+ this.store.dispatchInteraction('dblclick', this.slot().idEvent, {
2349
+ event: this.event(),
2350
+ slotId: this.slot().id,
2351
+ originalEvent: event
2352
+ });
2353
+ }
2354
+ onContextMenu(event) {
2355
+ event.preventDefault();
2356
+ event.stopPropagation();
2357
+ this.store.dispatchInteraction('contextmenu', this.slot().idEvent, {
2358
+ event: this.event(),
2359
+ slotId: this.slot().id,
2360
+ originalEvent: event
2361
+ });
2362
+ }
2363
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthSlot, deps: [], target: i0.ɵɵFactoryTarget.Component });
2364
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: MonthSlot, isStandalone: true, selector: "mglon-month-slot", inputs: { slot: { classPropertyName: "slot", publicName: "slot", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "style.position": "\"absolute\"", "style.top.px": "slot().position.top", "style.left.%": "slot().position.left", "style.width.%": "slot().position.width", "style.--slot-width.%": "slot().position.width", "style.height.px": "slot().position.height", "style.z-index": "slot().zIndex", "style.--slot-bg": "isAllDay() ? \"var(--mglon-all-day-bg, var(--mglon-schedule-neutral-200))\" : colorScheme().base", "style.--slot-hover": "isAllDay() ? \"var(--mglon-all-day-hover, var(--mglon-schedule-neutral-300))\" : colorScheme().hover", "style.--slot-text": "isAllDay() ? \"var(--mglon-all-day-text, var(--mglon-schedule-on-surface))\" : colorScheme().text", "style.--slot-text-hover": "isAllDay() ? \"var(--mglon-all-day-text-hover, var(--mglon-schedule-on-surface))\" : colorScheme().textHover", "style.--slot-radius": "slotRadius()", "attr.data-slot-type": "slot().type", "class.mglon-month-slot--first": "slot().type === \"first\"", "class.mglon-month-slot--last": "slot().type === \"last\"", "class.mglon-month-slot--middle": "slot().type === \"middle\"", "class.mglon-month-slot--full": "slot().type === \"full\"", "class.mglon-event--all-day": "isAllDay()", "class.mglon-month-slot--dragging": "isDragging()", "class.mglon-month-slot--idle": "!isDragging()", "class.mglon-month-slot--hovered": "isHovered()" } }, ngImport: i0, template: "<!-- Renderizado de slots recurrentes (Se eliminan los eventos de resize y drag) -->\n@if(isRecurrent()) {\n<div class=\"mglon-month-slot__inner\" [mglonZigzag]=\"zigzagSides()\" (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\" (click)=\"onClick($event)\" (dblclick)=\"onDblClick($event)\"\n (contextmenu)=\"onContextMenu($event)\">\n <span class=\"mglon-month-slot__title\" [mglonMonthRecurrence]=\"isRecurrent()\" [mglonAllDay]=\"isAllDay()\"\n [dotColor]=\"rawColor()\">{{ title() }}</span>\n</div>\n}\n\n<!-- Renderizado de slots no recurrentes -->\n@else {\n<div class=\"mglon-month-slot__inner\" [mglonZigzag]=\"zigzagSides()\" [mglonResizable]=\"resizableSides()\"\n (resizeStart)=\"onResizeStart($event)\" (pointerdown)=\"onPointerDown($event)\" (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\" (click)=\"onClick($event)\" (dblclick)=\"onDblClick($event)\"\n (contextmenu)=\"onContextMenu($event)\">\n <span class=\"mglon-month-slot__title\" [mglonAllDay]=\"isAllDay()\" [dotColor]=\"rawColor()\">{{ title() }}</span>\n</div>\n}", styles: [".mglon-zigzag-right{position:relative}.mglon-zigzag-right:after{content:\"\";position:absolute;height:var(--mglon-zigzag-size, 5px);background-color:var(--mglon-zigzag-color, white);z-index:1;--mglon-zigzag-mask-size: calc(var(--mglon-zigzag-size, 5px) * 1.6);--zigzag-mask: linear-gradient(225deg, #000 50%, transparent 0), linear-gradient(315deg, #000 50%, transparent 0);-webkit-mask:var(--zigzag-mask);mask:var(--zigzag-mask);-webkit-mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);-webkit-mask-composite:source-over;mask-composite:add;top:0;bottom:0;right:0;width:var(--mglon-zigzag-size, 5px);height:auto;transform:rotate(0)}.mglon-zigzag-left{position:relative}.mglon-zigzag-left:before{content:\"\";position:absolute;height:var(--mglon-zigzag-size, 5px);background-color:var(--mglon-zigzag-color, white);z-index:1;--mglon-zigzag-mask-size: calc(var(--mglon-zigzag-size, 5px) * 1.6);--zigzag-mask: linear-gradient(225deg, #000 50%, transparent 0), linear-gradient(315deg, #000 50%, transparent 0);-webkit-mask:var(--zigzag-mask);mask:var(--zigzag-mask);-webkit-mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);-webkit-mask-composite:source-over;mask-composite:add;top:0;bottom:0;left:0;width:var(--mglon-zigzag-size, 5px);height:auto;transform:rotate(180deg)}.mglon-zigzag-top{position:relative}.mglon-zigzag-top:before{content:\"\";position:absolute;width:var(--mglon-zigzag-size, 5px);background-color:var(--mglon-zigzag-color, white);z-index:1;--mglon-zigzag-mask-size: calc(var(--mglon-zigzag-size, 5px) * 1.6);--zigzag-mask: linear-gradient(225deg, #000 50%, transparent 0), linear-gradient(315deg, #000 50%, transparent 0);-webkit-mask:var(--zigzag-mask);mask:var(--zigzag-mask);-webkit-mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);-webkit-mask-composite:source-over;mask-composite:add;left:0;right:0;top:0;width:auto;height:var(--mglon-zigzag-size, 5px);transform:rotate(-90deg)}.mglon-zigzag-bottom{position:relative}.mglon-zigzag-bottom:after{content:\"\";position:absolute;width:var(--mglon-zigzag-size, 5px);background-color:var(--mglon-zigzag-color, white);z-index:1;--mglon-zigzag-mask-size: calc(var(--mglon-zigzag-size, 5px) * 1.6);--zigzag-mask: linear-gradient(225deg, #000 50%, transparent 0), linear-gradient(315deg, #000 50%, transparent 0);-webkit-mask:var(--zigzag-mask);mask:var(--zigzag-mask);-webkit-mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);-webkit-mask-composite:source-over;mask-composite:add;left:0;right:0;bottom:0;width:auto;height:var(--mglon-zigzag-size, 5px);transform:rotate(90deg)}.mglon-resizable-left:before{content:\"\";position:absolute;background-color:transparent;z-index:10;top:0;bottom:0;left:0;width:var(--mglon-resize-handle-size, 6px);cursor:col-resize}.mglon-resizable-right:after{content:\"\";position:absolute;background-color:transparent;z-index:10;top:0;bottom:0;right:0;width:var(--mglon-resize-handle-size, 6px);cursor:col-resize}.mglon-resizable-top:before{content:\"\";position:absolute;background-color:transparent;z-index:10;left:0;right:0;top:0;height:var(--mglon-resize-handle-size, 6px);cursor:row-resize}.mglon-resizable-bottom:after{content:\"\";position:absolute;background-color:transparent;z-index:10;left:0;right:0;bottom:0;height:var(--mglon-resize-handle-size, 6px);cursor:row-resize}:host{display:flex;align-items:center;box-sizing:border-box;border-radius:var(--slot-radius, var(--mglon-schedule-radius-sm));background-color:var(--slot-bg, #4285f4);color:var(--slot-text, #fff);font-size:12px;line-height:1;overflow:hidden;cursor:grab;transition:background-color .15s ease;touch-action:none}:host:active{cursor:grabbing}:host:hover{background-color:var(--slot-hover);color:var(--slot-text-hover, var(--slot-text))}:host{pointer-events:all}:host(.mglon-month-slot--full){width:calc(var(--slot-width, 100%) - 4px)!important}:host(.mglon-month-slot--first){border-top-right-radius:0;border-bottom-right-radius:0}:host(.mglon-month-slot--last){border-top-left-radius:0;border-bottom-left-radius:0;width:calc(var(--slot-width, 100%) - 4px)!important}:host(.mglon-month-slot--middle){border-radius:0}.mglon-month-slot__inner{display:flex;align-items:center;width:100%;height:100%;padding:0 8px;box-sizing:border-box}.mglon-month-slot__title{display:inline-flex;align-items:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%}.mglon-month-slot__title .mglon-month-slot__recurrence-icon{flex-shrink:0;margin-right:var(--mglon-schedule-gap-sm, 8px);font-size:1.2em}:host(.mglon-month-slot--idle){cursor:grab;pointer-events:all}:host(.mglon-month-slot--dragging){opacity:.6;cursor:grabbing;box-shadow:var(--mglon-schedule-shadow-lg, 0 10px 25px rgba(0, 0, 0, .15));transform:scale(1.02);z-index:100!important;pointer-events:none}:host(.mglon-month-slot--hovered){background-color:var(--slot-hover);color:var(--slot-text-hover, var(--slot-text))}:host(.mglon-event--all-day){box-shadow:none;border:var(--slot-border, none)}:host(.mglon-event--all-day):hover{border:var(--slot-border, none)}.mglon-event__all-day-icon{flex-shrink:0;margin-right:var(--mglon-schedule-gap-sm, 8px);font-size:1.2em;display:flex!important;align-items:center;justify-content:center}\n"], dependencies: [{ kind: "directive", type: ZigzagDirective, selector: "[mglonZigzag]", inputs: ["mglonZigzag", "zigzagSize"] }, { kind: "directive", type: ResizableDirective, selector: "[mglonResizable]", inputs: ["mglonResizable", "handleSize"], outputs: ["resizeStart"] }, { kind: "directive", type: MonthRecurrenceDirective, selector: "[mglonMonthRecurrence]", inputs: ["mglonMonthRecurrence"] }, { kind: "directive", type: AllDayDirective, selector: "[mglonAllDay]", inputs: ["mglonAllDay", "dotColor"] }] });
2365
+ }
2366
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthSlot, decorators: [{
2367
+ type: Component,
2368
+ args: [{ selector: 'mglon-month-slot', imports: [ZigzagDirective, ResizableDirective, MonthRecurrenceDirective, AllDayDirective], host: {
2369
+ '[style.position]': '"absolute"',
2370
+ '[style.top.px]': 'slot().position.top',
2371
+ '[style.left.%]': 'slot().position.left',
2372
+ '[style.width.%]': 'slot().position.width',
2373
+ '[style.--slot-width.%]': 'slot().position.width',
2374
+ '[style.height.px]': 'slot().position.height',
2375
+ '[style.z-index]': 'slot().zIndex',
2376
+ '[style.--slot-bg]': 'isAllDay() ? "var(--mglon-all-day-bg, var(--mglon-schedule-neutral-200))" : colorScheme().base',
2377
+ '[style.--slot-hover]': 'isAllDay() ? "var(--mglon-all-day-hover, var(--mglon-schedule-neutral-300))" : colorScheme().hover',
2378
+ '[style.--slot-text]': 'isAllDay() ? "var(--mglon-all-day-text, var(--mglon-schedule-on-surface))" : colorScheme().text',
2379
+ '[style.--slot-text-hover]': 'isAllDay() ? "var(--mglon-all-day-text-hover, var(--mglon-schedule-on-surface))" : colorScheme().textHover',
2380
+ '[style.--slot-radius]': 'slotRadius()',
2381
+ '[attr.data-slot-type]': 'slot().type',
2382
+ '[class.mglon-month-slot--first]': 'slot().type === "first"',
2383
+ '[class.mglon-month-slot--last]': 'slot().type === "last"',
2384
+ '[class.mglon-month-slot--middle]': 'slot().type === "middle"',
2385
+ '[class.mglon-month-slot--full]': 'slot().type === "full"',
2386
+ '[class.mglon-event--all-day]': 'isAllDay()',
2387
+ '[class.mglon-month-slot--dragging]': 'isDragging()',
2388
+ '[class.mglon-month-slot--idle]': '!isDragging()',
2389
+ '[class.mglon-month-slot--hovered]': 'isHovered()',
2390
+ }, template: "<!-- Renderizado de slots recurrentes (Se eliminan los eventos de resize y drag) -->\n@if(isRecurrent()) {\n<div class=\"mglon-month-slot__inner\" [mglonZigzag]=\"zigzagSides()\" (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\" (click)=\"onClick($event)\" (dblclick)=\"onDblClick($event)\"\n (contextmenu)=\"onContextMenu($event)\">\n <span class=\"mglon-month-slot__title\" [mglonMonthRecurrence]=\"isRecurrent()\" [mglonAllDay]=\"isAllDay()\"\n [dotColor]=\"rawColor()\">{{ title() }}</span>\n</div>\n}\n\n<!-- Renderizado de slots no recurrentes -->\n@else {\n<div class=\"mglon-month-slot__inner\" [mglonZigzag]=\"zigzagSides()\" [mglonResizable]=\"resizableSides()\"\n (resizeStart)=\"onResizeStart($event)\" (pointerdown)=\"onPointerDown($event)\" (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\" (click)=\"onClick($event)\" (dblclick)=\"onDblClick($event)\"\n (contextmenu)=\"onContextMenu($event)\">\n <span class=\"mglon-month-slot__title\" [mglonAllDay]=\"isAllDay()\" [dotColor]=\"rawColor()\">{{ title() }}</span>\n</div>\n}", styles: [".mglon-zigzag-right{position:relative}.mglon-zigzag-right:after{content:\"\";position:absolute;height:var(--mglon-zigzag-size, 5px);background-color:var(--mglon-zigzag-color, white);z-index:1;--mglon-zigzag-mask-size: calc(var(--mglon-zigzag-size, 5px) * 1.6);--zigzag-mask: linear-gradient(225deg, #000 50%, transparent 0), linear-gradient(315deg, #000 50%, transparent 0);-webkit-mask:var(--zigzag-mask);mask:var(--zigzag-mask);-webkit-mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);-webkit-mask-composite:source-over;mask-composite:add;top:0;bottom:0;right:0;width:var(--mglon-zigzag-size, 5px);height:auto;transform:rotate(0)}.mglon-zigzag-left{position:relative}.mglon-zigzag-left:before{content:\"\";position:absolute;height:var(--mglon-zigzag-size, 5px);background-color:var(--mglon-zigzag-color, white);z-index:1;--mglon-zigzag-mask-size: calc(var(--mglon-zigzag-size, 5px) * 1.6);--zigzag-mask: linear-gradient(225deg, #000 50%, transparent 0), linear-gradient(315deg, #000 50%, transparent 0);-webkit-mask:var(--zigzag-mask);mask:var(--zigzag-mask);-webkit-mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);-webkit-mask-composite:source-over;mask-composite:add;top:0;bottom:0;left:0;width:var(--mglon-zigzag-size, 5px);height:auto;transform:rotate(180deg)}.mglon-zigzag-top{position:relative}.mglon-zigzag-top:before{content:\"\";position:absolute;width:var(--mglon-zigzag-size, 5px);background-color:var(--mglon-zigzag-color, white);z-index:1;--mglon-zigzag-mask-size: calc(var(--mglon-zigzag-size, 5px) * 1.6);--zigzag-mask: linear-gradient(225deg, #000 50%, transparent 0), linear-gradient(315deg, #000 50%, transparent 0);-webkit-mask:var(--zigzag-mask);mask:var(--zigzag-mask);-webkit-mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);-webkit-mask-composite:source-over;mask-composite:add;left:0;right:0;top:0;width:auto;height:var(--mglon-zigzag-size, 5px);transform:rotate(-90deg)}.mglon-zigzag-bottom{position:relative}.mglon-zigzag-bottom:after{content:\"\";position:absolute;width:var(--mglon-zigzag-size, 5px);background-color:var(--mglon-zigzag-color, white);z-index:1;--mglon-zigzag-mask-size: calc(var(--mglon-zigzag-size, 5px) * 1.6);--zigzag-mask: linear-gradient(225deg, #000 50%, transparent 0), linear-gradient(315deg, #000 50%, transparent 0);-webkit-mask:var(--zigzag-mask);mask:var(--zigzag-mask);-webkit-mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);mask-size:var(--mglon-zigzag-mask-size) var(--mglon-zigzag-mask-size);-webkit-mask-composite:source-over;mask-composite:add;left:0;right:0;bottom:0;width:auto;height:var(--mglon-zigzag-size, 5px);transform:rotate(90deg)}.mglon-resizable-left:before{content:\"\";position:absolute;background-color:transparent;z-index:10;top:0;bottom:0;left:0;width:var(--mglon-resize-handle-size, 6px);cursor:col-resize}.mglon-resizable-right:after{content:\"\";position:absolute;background-color:transparent;z-index:10;top:0;bottom:0;right:0;width:var(--mglon-resize-handle-size, 6px);cursor:col-resize}.mglon-resizable-top:before{content:\"\";position:absolute;background-color:transparent;z-index:10;left:0;right:0;top:0;height:var(--mglon-resize-handle-size, 6px);cursor:row-resize}.mglon-resizable-bottom:after{content:\"\";position:absolute;background-color:transparent;z-index:10;left:0;right:0;bottom:0;height:var(--mglon-resize-handle-size, 6px);cursor:row-resize}:host{display:flex;align-items:center;box-sizing:border-box;border-radius:var(--slot-radius, var(--mglon-schedule-radius-sm));background-color:var(--slot-bg, #4285f4);color:var(--slot-text, #fff);font-size:12px;line-height:1;overflow:hidden;cursor:grab;transition:background-color .15s ease;touch-action:none}:host:active{cursor:grabbing}:host:hover{background-color:var(--slot-hover);color:var(--slot-text-hover, var(--slot-text))}:host{pointer-events:all}:host(.mglon-month-slot--full){width:calc(var(--slot-width, 100%) - 4px)!important}:host(.mglon-month-slot--first){border-top-right-radius:0;border-bottom-right-radius:0}:host(.mglon-month-slot--last){border-top-left-radius:0;border-bottom-left-radius:0;width:calc(var(--slot-width, 100%) - 4px)!important}:host(.mglon-month-slot--middle){border-radius:0}.mglon-month-slot__inner{display:flex;align-items:center;width:100%;height:100%;padding:0 8px;box-sizing:border-box}.mglon-month-slot__title{display:inline-flex;align-items:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%}.mglon-month-slot__title .mglon-month-slot__recurrence-icon{flex-shrink:0;margin-right:var(--mglon-schedule-gap-sm, 8px);font-size:1.2em}:host(.mglon-month-slot--idle){cursor:grab;pointer-events:all}:host(.mglon-month-slot--dragging){opacity:.6;cursor:grabbing;box-shadow:var(--mglon-schedule-shadow-lg, 0 10px 25px rgba(0, 0, 0, .15));transform:scale(1.02);z-index:100!important;pointer-events:none}:host(.mglon-month-slot--hovered){background-color:var(--slot-hover);color:var(--slot-text-hover, var(--slot-text))}:host(.mglon-event--all-day){box-shadow:none;border:var(--slot-border, none)}:host(.mglon-event--all-day):hover{border:var(--slot-border, none)}.mglon-event__all-day-icon{flex-shrink:0;margin-right:var(--mglon-schedule-gap-sm, 8px);font-size:1.2em;display:flex!important;align-items:center;justify-content:center}\n"] }]
2391
+ }], propDecorators: { slot: [{ type: i0.Input, args: [{ isSignal: true, alias: "slot", required: true }] }] } });
2392
+
2393
+ class MonthWeekEventsContainer {
2394
+ slots = input.required(...(ngDevMode ? [{ debugName: "slots" }] : []));
2395
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthWeekEventsContainer, deps: [], target: i0.ɵɵFactoryTarget.Component });
2396
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: MonthWeekEventsContainer, isStandalone: true, selector: "mglon-month-week-events-container", inputs: { slots: { classPropertyName: "slots", publicName: "slots", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "style.position": "\"relative\"", "style.width": "\"100%\"", "style.height": "\"100%\"" } }, ngImport: i0, template: "<div class=\"mglon-month-week-events-container\">\n @for (slot of slots(); track slot.id) {\n <mglon-month-slot [slot]=\"slot\"></mglon-month-slot>\n }\n</div>", styles: [".mglon-month-week-events-container{position:relative;display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "component", type: MonthSlot, selector: "mglon-month-slot", inputs: ["slot"] }] });
2397
+ }
2398
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthWeekEventsContainer, decorators: [{
2399
+ type: Component,
2400
+ args: [{ selector: 'mglon-month-week-events-container', imports: [MonthSlot], host: {
2401
+ '[style.position]': '"relative"',
2402
+ '[style.width]': '"100%"',
2403
+ '[style.height]': '"100%"',
2404
+ }, template: "<div class=\"mglon-month-week-events-container\">\n @for (slot of slots(); track slot.id) {\n <mglon-month-slot [slot]=\"slot\"></mglon-month-slot>\n }\n</div>", styles: [".mglon-month-week-events-container{position:relative;display:block;width:100%;height:100%}\n"] }]
2405
+ }], propDecorators: { slots: [{ type: i0.Input, args: [{ isSignal: true, alias: "slots", required: true }] }] } });
2406
+
2407
+ class EventRendererAdapter {
2408
+ }
2409
+
2410
+ const DEFAULT_SLOT_CONFIG = {
2411
+ slotHeight: SLOT_HEIGHT,
2412
+ slotGap: SLOT_GAP
2413
+ };
2414
+ /**
2415
+ * Calculates the minimum height required to display a given number of event rows.
2416
+ *
2417
+ * Formula: (n * slotHeight) + ((n - 1) * slotGap)
2418
+ * - For n=3 with defaults (20px height, 2px gap):
2419
+ * (3 * 20) + (2 * 2) = 60 + 4 = 64px
2420
+ *
2421
+ * @param rows - Number of event rows to display
2422
+ * @param config - Slot configuration (height and gap)
2423
+ * @returns Minimum height in pixels
2424
+ */
2425
+ function calculateMinSlotContainerHeight(rows, config = DEFAULT_SLOT_CONFIG) {
2426
+ if (rows <= 0)
2427
+ return 0;
2428
+ const totalSlotHeight = rows * config.slotHeight;
2429
+ const totalGapHeight = (rows - 1) * config.slotGap;
2430
+ return totalSlotHeight + totalGapHeight;
2431
+ }
2432
+
2433
+ /**
2434
+ * Extracts the effective date range from any event type.
2435
+ * Normalizes different event structures into a consistent DateRange.
2436
+ */
2437
+ function getEventDateRange(event) {
2438
+ if (isEvent(event) || isRecurrentEvent(event)) {
2439
+ return {
2440
+ start: startOfDay(event.start),
2441
+ end: endOfDay(event.end)
2442
+ };
2443
+ }
2444
+ if (isAllDayEvent(event)) {
2445
+ const start = startOfDay(event.date);
2446
+ const end = event.endDate ? endOfDay(event.endDate) : endOfDay(event.date);
2447
+ return { start, end };
2448
+ }
2449
+ // Fallback (should never happen with proper types)
2450
+ return { start: new Date(), end: new Date() };
2451
+ }
2452
+
2453
+ /**
2454
+ * Determines the slot type based on whether the event extends beyond the week boundaries.
2455
+ *
2456
+ * @returns
2457
+ * - 'full': Event starts and ends within this week
2458
+ * - 'first': Event starts this week but continues into next week(s)
2459
+ * - 'last': Event started in previous week(s) and ends this week
2460
+ * - 'middle': Event spans across this entire week (started before, ends after)
2461
+ */
2462
+ function determineSlotType(eventRange, weekRange) {
2463
+ const startsBeforeWeek = eventRange.start < weekRange.start;
2464
+ const endsAfterWeek = eventRange.end > weekRange.end;
2465
+ if (startsBeforeWeek && endsAfterWeek)
2466
+ return 'middle';
2467
+ if (startsBeforeWeek)
2468
+ return 'last';
2469
+ if (endsAfterWeek)
2470
+ return 'first';
2471
+ return 'full';
2472
+ }
2473
+
2474
+ const DAYS_IN_WEEK$1 = 7;
2475
+ const PERCENTAGE_BASE = 100;
2476
+ /**
2477
+ * Calculates the horizontal position as percentages (0-100).
2478
+ * No container dimensions needed - pure percentage-based positioning.
2479
+ */
2480
+ function calculateHorizontalPosition(clampedStart, clampedEnd, weekStart) {
2481
+ // Calculate day indices (0-6) within the week
2482
+ const startDayIndex = differenceInDays(startOfDay(clampedStart), startOfDay(weekStart));
2483
+ const endDayIndex = differenceInDays(startOfDay(clampedEnd), startOfDay(weekStart));
2484
+ // Number of days the event spans (inclusive)
2485
+ const spanDays = endDayIndex - startDayIndex + 1;
2486
+ return {
2487
+ left: (startDayIndex / DAYS_IN_WEEK$1) * PERCENTAGE_BASE,
2488
+ width: (spanDays / DAYS_IN_WEEK$1) * PERCENTAGE_BASE
2489
+ };
2490
+ }
2491
+ /**
2492
+ * Finds the first available row for an event without overlapping.
2493
+ * Uses a greedy algorithm with proper day-range intersection checking.
2494
+ *
2495
+ * ## How overlap is determined:
2496
+ *
2497
+ * Two events overlap if their day ranges intersect. We use half-open intervals
2498
+ * [startDay, exclusiveEndDay) for comparison:
2499
+ *
2500
+ * ```
2501
+ * Event A: Dec 22-24 → { startDay: Dec 22, exclusiveEndDay: Dec 25 }
2502
+ * Event B: Dec 25 → { startDay: Dec 25, exclusiveEndDay: Dec 26 }
2503
+ *
2504
+ * Intersection check: A.start < B.exclusiveEnd AND B.start < A.exclusiveEnd
2505
+ * Dec 22 < Dec 26 (true) AND Dec 25 < Dec 25 (false)
2506
+ * → NO overlap ✓
2507
+ * ```
2508
+ *
2509
+ * @param clampedStart - Event start date (clamped to week)
2510
+ * @param clampedEnd - Event end date (clamped to week)
2511
+ * @param rowAssignments - Map of row index → array of placed event ranges
2512
+ */
2513
+ function findAvailableRow(clampedStart, clampedEnd, rowAssignments) {
2514
+ let rowIndex = 0;
2515
+ // Create the day range for the new event
2516
+ const newEventRange = {
2517
+ startDay: startOfDay(clampedStart),
2518
+ exclusiveEndDay: addDays(startOfDay(clampedEnd), 1)
2519
+ };
2520
+ while (true) {
2521
+ const existingRanges = rowAssignments.get(rowIndex) || [];
2522
+ // Check if new event overlaps with any existing event in this row
2523
+ // Using half-open interval intersection: A ∩ B ≠ ∅ ⟺ A.start < B.end AND B.start < A.end
2524
+ const hasOverlap = existingRanges.some(existing => newEventRange.startDay < existing.exclusiveEndDay &&
2525
+ existing.startDay < newEventRange.exclusiveEndDay);
2526
+ if (!hasOverlap) {
2527
+ rowAssignments.set(rowIndex, [...existingRanges, newEventRange]);
2528
+ return rowIndex;
2529
+ }
2530
+ rowIndex++;
2531
+ }
2532
+ }
2533
+ /**
2534
+ * Calculates the maximum number of visible rows based on container height.
2535
+ *
2536
+ * @param containerHeight - Height of the container in pixels
2537
+ * @param slotHeight - Height of each slot row in pixels
2538
+ * @param slotGap - Vertical gap between slots in pixels
2539
+ * @returns Maximum number of full rows that fit in the container
2540
+ *
2541
+ * @example
2542
+ * // Container of 66px with 20px slots and 2px gap = 3 rows
2543
+ * // Row 0: 0-20px, Row 1: 22-42px, Row 2: 44-64px
2544
+ * calculateMaxVisibleRows(66, 20, 2) // returns 3
2545
+ */
2546
+ function calculateMaxVisibleRows(containerHeight, slotHeight, slotGap) {
2547
+ if (containerHeight <= 0 || slotHeight <= 0) {
2548
+ return 0;
2549
+ }
2550
+ // Each row takes: slotHeight + slotGap (except the last one doesn't need gap)
2551
+ // Formula: containerHeight >= (n * slotHeight) + ((n - 1) * slotGap)
2552
+ // Simplified: n = floor((containerHeight + slotGap) / (slotHeight + slotGap))
2553
+ const rowWithGap = slotHeight + slotGap;
2554
+ return Math.floor((containerHeight + slotGap) / rowWithGap);
2555
+ }
2556
+
2557
+ /**
2558
+ * Slices a collection of events into visual slots for a specific week.
2559
+ * Returns percentage-based horizontal positions (no ResizeObserver needed).
2560
+ *
2561
+ * @param events - Array of events to process (should already be filtered for the week)
2562
+ * @param weekRange - The date range of the week { start: Sunday 00:00, end: Saturday 23:59 }
2563
+ * @param config - Optional configuration for slot sizing
2564
+ * @returns Array of SlotModel with calculated positions (left/width as %, top/height as px)
2565
+ *
2566
+ * @example
2567
+ * const slots = sliceEventsByWeek(events, {
2568
+ * start: startOfWeek(date),
2569
+ * end: endOfWeek(date)
2570
+ * })
2571
+ * // Use in template: [style.left.%]="slot.position.left"
2572
+ */
2573
+ function sliceEventsByWeek(events, weekRange, config = DEFAULT_SLOT_CONFIG) {
2574
+ const slots = [];
2575
+ const rowAssignments = new Map();
2576
+ for (const event of events) {
2577
+ const eventRange = getEventDateRange(event);
2578
+ // Clamp event dates to the week boundaries
2579
+ const clampedStart = max([eventRange.start, weekRange.start]);
2580
+ const clampedEnd = min([eventRange.end, weekRange.end]);
2581
+ // Calculate horizontal position as percentages
2582
+ const { left, width } = calculateHorizontalPosition(clampedStart, clampedEnd, weekRange.start);
2583
+ // Find available row (avoids overlapping)
2584
+ const rowIndex = findAvailableRow(clampedStart, clampedEnd, rowAssignments);
2585
+ // Calculate vertical position in pixels
2586
+ const top = rowIndex * (config.slotHeight + config.slotGap);
2587
+ // Determine slot type based on week boundaries
2588
+ const type = determineSlotType(eventRange, weekRange);
2589
+ // Extract event properties for the slot
2590
+ const isEditable = !event.isReadOnly && !event.isBlocked;
2591
+ slots.push({
2592
+ id: `${event.id}-${weekRange.start.getTime()}`,
2593
+ idEvent: event.id,
2594
+ start: clampedStart,
2595
+ end: clampedEnd,
2596
+ position: {
2597
+ top,
2598
+ left,
2599
+ height: config.slotHeight,
2600
+ width
2601
+ },
2602
+ zIndex: rowIndex + 1,
2603
+ type,
2604
+ color: event.color ?? '',
2605
+ draggable: isEditable,
2606
+ resizable: isEditable
2607
+ });
2608
+ }
2609
+ return slots;
2610
+ }
2611
+
2612
+ // ============================================================================
2613
+ // CONSTANTS
2614
+ // ============================================================================
2615
+ const DAYS_IN_WEEK = 7;
2616
+ // ============================================================================
2617
+ // MAIN FUNCTION
2618
+ // ============================================================================
2619
+ /**
2620
+ * Partitions slots into visible and hidden based on the maximum number of rows
2621
+ * that can fit in the container.
2622
+ *
2623
+ * ## How it works:
2624
+ *
2625
+ * 1. Each slot has a `zIndex` which represents its row position (1-indexed).
2626
+ * - zIndex 1 = Row 0 (top row)
2627
+ * - zIndex 2 = Row 1
2628
+ * - etc.
2629
+ *
2630
+ * 2. If a slot's zIndex exceeds maxRows, the entire slot is hidden.
2631
+ * This ensures multi-day events don't partially render.
2632
+ *
2633
+ * 3. For hidden slots, we count how many events are hidden per day column.
2634
+ * This allows showing "+N more" indicators in each affected day.
2635
+ *
2636
+ * ## Visual Example:
2637
+ *
2638
+ * ```
2639
+ * maxRows = 2
2640
+ *
2641
+ * Mon Tue Wed Thu Fri
2642
+ * ┌─────┬─────┬─────┬─────┬─────┐
2643
+ * Row 0 │ A │ A │ A │ │ │ visible (zIndex=1)
2644
+ * ├─────┼─────┼─────┼─────┼─────┤
2645
+ * Row 1 │ │ B │ B │ B │ │ visible (zIndex=2)
2646
+ * ├─────┴─────┴─────┴─────┴─────┤ ← maxRows limit
2647
+ * Row 2 │ │ │ C │ │ │ HIDDEN (zIndex=3)
2648
+ * └─────┴─────┴─────┴─────┴─────┘
2649
+ *
2650
+ * overflowPerDay = [0, 0, 1, 0, 0, 0, 0]
2651
+ * ↑ Wed shows "+1 more"
2652
+ * ```
2653
+ *
2654
+ * @param slots - All slots generated by sliceEventsByWeek
2655
+ * @param maxRows - Maximum number of rows that fit in the container
2656
+ * @param weekStart - Start date of the week (used to calculate day indices)
2657
+ * @returns Partitioned slots with overflow counts per day
2658
+ */
2659
+ function partitionSlotsByVisibility(slots, maxRows, weekStart) {
2660
+ const visibleSlots = [];
2661
+ const hiddenSlots = [];
2662
+ const overflowPerDay = new Array(DAYS_IN_WEEK).fill(0);
2663
+ // Normalize week start to beginning of day for consistent comparisons
2664
+ const normalizedWeekStart = startOfDay(weekStart);
2665
+ for (const slot of slots) {
2666
+ // zIndex is 1-indexed (row 0 has zIndex 1)
2667
+ // A slot is visible if its row fits within maxRows
2668
+ const isVisible = slot.zIndex <= maxRows;
2669
+ if (isVisible) {
2670
+ visibleSlots.push(slot);
2671
+ }
2672
+ else {
2673
+ hiddenSlots.push(slot);
2674
+ // Calculate which days this hidden slot spans
2675
+ // and increment the overflow counter for each affected day
2676
+ incrementOverflowForSlotDays(slot, normalizedWeekStart, overflowPerDay);
2677
+ }
2678
+ }
2679
+ return { visibleSlots, hiddenSlots, overflowPerDay };
2680
+ }
2681
+ // ============================================================================
2682
+ // HELPER FUNCTIONS
2683
+ // ============================================================================
2684
+ /**
2685
+ * Increments the overflow counter for each day that a slot occupies.
2686
+ *
2687
+ * This ensures that the "+N more" indicator correctly reflects
2688
+ * all hidden events affecting each day column.
2689
+ *
2690
+ * @param slot - The hidden slot
2691
+ * @param weekStart - Normalized start of the week
2692
+ * @param overflowPerDay - Array to mutate with incremented counts
2693
+ */
2694
+ function incrementOverflowForSlotDays(slot, weekStart, overflowPerDay) {
2695
+ // Calculate day indices (0-6) within the week
2696
+ const startDayIndex = differenceInDays(startOfDay(slot.start), weekStart);
2697
+ const endDayIndex = differenceInDays(startOfDay(slot.end), weekStart);
2698
+ // Clamp indices to valid range [0, 6]
2699
+ // This handles edge cases where slot dates might extend beyond the week
2700
+ const safeStartDay = Math.max(0, Math.min(startDayIndex, DAYS_IN_WEEK - 1));
2701
+ const safeEndDay = Math.max(0, Math.min(endDayIndex, DAYS_IN_WEEK - 1));
2702
+ // Increment counter for each day the slot spans
2703
+ for (let day = safeStartDay; day <= safeEndDay; day++) {
2704
+ overflowPerDay[day]++;
2705
+ }
2706
+ }
2707
+
2708
+ // Public API
2709
+
2710
+ class MonthWeek extends EventRendererAdapter {
2711
+ store = inject(CalendarStore);
2712
+ week = input.required(...(ngDevMode ? [{ debugName: "week" }] : []));
2713
+ /** Index of this week in the month (0-based) */
2714
+ weekIndex = input(0, ...(ngDevMode ? [{ debugName: "weekIndex" }] : []));
2715
+ /** Whether this week is expanded to show all events */
2716
+ expanded = input(false, ...(ngDevMode ? [{ debugName: "expanded" }] : []));
2717
+ top = CELL_HEADER_HEIGHT;
2718
+ /** Minimum height for the week row from store config */
2719
+ minHeight = this.store.minWeekRowHeight;
2720
+ /**
2721
+ * Computes the week's date range from first to last day.
2722
+ */
2723
+ weekRange = computed(() => {
2724
+ const weekDays = this.week().days;
2725
+ if (weekDays.length === 0) {
2726
+ return { start: new Date(), end: new Date() };
2727
+ }
2728
+ return {
2729
+ start: startOfDay(weekDays[0].date),
2730
+ end: endOfDay(weekDays[weekDays.length - 1].date)
2731
+ };
2732
+ }, ...(ngDevMode ? [{ debugName: "weekRange" }] : []));
2733
+ /**
2734
+ * Available height for event slots (total row height minus cell header).
2735
+ */
2736
+ availableHeight = computed(() => {
2737
+ return Math.max(0, this.store.weekRowHeight() - CELL_HEADER_HEIGHT);
2738
+ }, ...(ngDevMode ? [{ debugName: "availableHeight" }] : []));
2739
+ /**
2740
+ * Filters currentViewEvents to only include events
2741
+ * that intersect with this week's date range.
2742
+ */
2743
+ events = computed(() => {
2744
+ const weekDays = this.week().days;
2745
+ if (weekDays.length === 0)
2746
+ return [];
2747
+ return this.filterEvents(this.store.currentViewEvents(), this.weekRange());
2748
+ }, ...(ngDevMode ? [{ debugName: "events" }] : []));
2749
+ /**
2750
+ * Computed slots for this week's events.
2751
+ */
2752
+ slots = computed(() => {
2753
+ return this.createSlots(this.events());
2754
+ }, ...(ngDevMode ? [{ debugName: "slots" }] : []));
2755
+ /**
2756
+ * Maximum row number used by any slot.
2757
+ * Derived from the slot's top position.
2758
+ */
2759
+ maxRow = computed(() => {
2760
+ const allSlots = this.slots();
2761
+ if (allSlots.length === 0)
2762
+ return 0;
2763
+ // Calculate row from top position: row = top / (SLOT_HEIGHT + SLOT_GAP)
2764
+ const rowHeight = SLOT_HEIGHT + SLOT_GAP;
2765
+ return Math.max(...allSlots.map(slot => Math.floor(slot.position.top / rowHeight)));
2766
+ }, ...(ngDevMode ? [{ debugName: "maxRow" }] : []));
2767
+ /**
2768
+ * Height needed to show all events when expanded.
2769
+ * Calculated based on the number of slot rows plus bottom padding.
2770
+ */
2771
+ expandedHeight = computed(() => {
2772
+ const rows = this.maxRow() + 1; // rows are 0-indexed
2773
+ // Formula: header + (rows * slot height) + (rows * gap)
2774
+ return CELL_HEADER_HEIGHT + (rows * SLOT_HEIGHT) + (rows * SLOT_GAP);
2775
+ }, ...(ngDevMode ? [{ debugName: "expandedHeight" }] : []));
2776
+ /**
2777
+ * Whether the week has more events than can fit in the minimum height.
2778
+ * Used to conditionally show the expand toggle.
2779
+ */
2780
+ hasOverflow = computed(() => {
2781
+ return this.expandedHeight() > this.minHeight();
2782
+ }, ...(ngDevMode ? [{ debugName: "hasOverflow" }] : []));
2783
+ /**
2784
+ * Dynamic height for the week based on expansion state.
2785
+ * Returns the expanded height or minimum height.
2786
+ */
2787
+ weekHeight = computed(() => {
2788
+ if (this.expanded()) {
2789
+ // When expanded, use the larger of expandedHeight or minHeight
2790
+ return Math.max(this.expandedHeight(), this.minHeight());
2791
+ }
2792
+ return null; // Use min-height only (CSS handles it)
2793
+ }, ...(ngDevMode ? [{ debugName: "weekHeight" }] : []));
2794
+ filterEvents(events, dateRange) {
2795
+ return events.filter(event => isEventInRange(event, dateRange));
2796
+ }
2797
+ createSlots(events) {
2798
+ if (events.length === 0)
2799
+ return [];
2800
+ return sliceEventsByWeek(events, this.weekRange());
2801
+ }
2802
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthWeek, deps: null, target: i0.ɵɵFactoryTarget.Component });
2803
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: MonthWeek, isStandalone: true, selector: "mglon-month-week", inputs: { week: { classPropertyName: "week", publicName: "week", isSignal: true, isRequired: true, transformFunction: null }, weekIndex: { classPropertyName: "weekIndex", publicName: "weekIndex", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.height.px": "weekHeight()", "class.mglon-month-week--expanded": "expanded()" } }, usesInheritance: true, ngImport: i0, template: "<div class=\"mglon-month-week\" [class.mglon-month-week--expanded]=\"expanded()\">\n\n <div class=\"mglon-month-week__days\" [style.min-height.px]=\"minHeight()\">\n @for (day of week().days; track day.date.getTime()) {\n <mglon-month-cell [day]=\"day\" [attr.data-date]=\"day.date.toISOString()\"></mglon-month-cell>\n }\n </div>\n\n <div #weekEventsContainer class=\"mglon-month-week__events\" [style.top.px]=\"top\">\n <mglon-month-week-events-container [slots]=\"slots()\"></mglon-month-week-events-container>\n </div>\n</div>", styles: [".mglon-month-week{position:relative;height:100%}.mglon-month-week__days{display:var(--mglon-schedule-month-week-display);grid-template-columns:var(--mglon-schedule-month-week-columns);min-height:var(--mglon-schedule-month-week-min-height);height:100%;border-bottom:var(--mglon-schedule-month-week-border-bottom-width) solid var(--mglon-schedule-border)}.mglon-month-week__days:last-child{border-bottom:none}.mglon-month-week__events{position:absolute;inset:0;overflow:hidden;pointer-events:none}:host{display:block}:host(.mglon-month-week--expanded) .mglon-month-week__events{overflow:visible}\n"], dependencies: [{ kind: "component", type: MonthCell, selector: "mglon-month-cell", inputs: ["day"] }, { kind: "component", type: MonthWeekEventsContainer, selector: "mglon-month-week-events-container", inputs: ["slots"] }] });
2804
+ }
2805
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthWeek, decorators: [{
2806
+ type: Component,
2807
+ args: [{ selector: 'mglon-month-week', imports: [MonthCell, MonthWeekEventsContainer], host: {
2808
+ '[style.height.px]': 'weekHeight()',
2809
+ '[class.mglon-month-week--expanded]': 'expanded()'
2810
+ }, template: "<div class=\"mglon-month-week\" [class.mglon-month-week--expanded]=\"expanded()\">\n\n <div class=\"mglon-month-week__days\" [style.min-height.px]=\"minHeight()\">\n @for (day of week().days; track day.date.getTime()) {\n <mglon-month-cell [day]=\"day\" [attr.data-date]=\"day.date.toISOString()\"></mglon-month-cell>\n }\n </div>\n\n <div #weekEventsContainer class=\"mglon-month-week__events\" [style.top.px]=\"top\">\n <mglon-month-week-events-container [slots]=\"slots()\"></mglon-month-week-events-container>\n </div>\n</div>", styles: [".mglon-month-week{position:relative;height:100%}.mglon-month-week__days{display:var(--mglon-schedule-month-week-display);grid-template-columns:var(--mglon-schedule-month-week-columns);min-height:var(--mglon-schedule-month-week-min-height);height:100%;border-bottom:var(--mglon-schedule-month-week-border-bottom-width) solid var(--mglon-schedule-border)}.mglon-month-week__days:last-child{border-bottom:none}.mglon-month-week__events{position:absolute;inset:0;overflow:hidden;pointer-events:none}:host{display:block}:host(.mglon-month-week--expanded) .mglon-month-week__events{overflow:visible}\n"] }]
2811
+ }], propDecorators: { week: [{ type: i0.Input, args: [{ isSignal: true, alias: "week", required: true }] }], weekIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "weekIndex", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }] } });
2812
+
2813
+ class ResizeObserverDirective {
2814
+ elementRef;
2815
+ mglonResizeObserver = new EventEmitter();
2816
+ observer = null;
2817
+ constructor(elementRef) {
2818
+ this.elementRef = elementRef;
2819
+ }
2820
+ ngOnInit() {
2821
+ this.observer = new ResizeObserver((entries) => {
2822
+ const entry = entries[0];
2823
+ if (entry) {
2824
+ this.mglonResizeObserver.emit({
2825
+ width: entry.contentRect.width,
2826
+ height: entry.contentRect.height,
2827
+ entry
2828
+ });
2829
+ }
2830
+ });
2831
+ this.observer.observe(this.elementRef.nativeElement);
2832
+ }
2833
+ ngOnDestroy() {
2834
+ this.observer?.disconnect();
2835
+ this.observer = null;
2836
+ }
2837
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResizeObserverDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
2838
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: ResizeObserverDirective, isStandalone: true, selector: "[mglonResizeObserver]", outputs: { mglonResizeObserver: "mglonResizeObserver" }, ngImport: i0 });
2839
+ }
2840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResizeObserverDirective, decorators: [{
2841
+ type: Directive,
2842
+ args: [{
2843
+ selector: '[mglonResizeObserver]',
2844
+ standalone: true
2845
+ }]
2846
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { mglonResizeObserver: [{
2847
+ type: Output
2848
+ }] } });
2849
+
2850
+ /**
2851
+ * Month calendar grid component that displays a full month view with weeks and days.
2852
+ *
2853
+ * Implements the Selectable interface to enable date selection via mouse interaction.
2854
+ * Uses the SelectableDirective to handle all selection logic.
2855
+ *
2856
+ * Features:
2857
+ * - Displays 4-6 weeks dynamically
2858
+ * - Shows padding days from previous/next months
2859
+ * - Supports mouse-based date range selection
2860
+ * - Expandable week rows via toggle buttons
2861
+ *
2862
+ * @example
2863
+ * ```html
2864
+ * <mglon-month-grid
2865
+ * [currentDate]="selectedDate"
2866
+ * (selectionEnd)="onDateRangeSelected($event)">
2867
+ * </mglon-month-grid>
2868
+ * ```
2869
+ */
2870
+ class MonthGrid {
2871
+ elementRef = inject(ElementRef);
2872
+ store = inject(CalendarStore);
2873
+ /**
2874
+ * Reference to the SelectableDirective instance.
2875
+ * Used to access selection state (selection rectangle and isSelecting flag).
2876
+ */
2877
+ selectableDirective = viewChild(SelectableDirective, ...(ngDevMode ? [{ debugName: "selectableDirective" }] : []));
2878
+ /**
2879
+ * The date to display the calendar for.
2880
+ * Can be any date within the target month.
2881
+ * @default new Date() (current month)
2882
+ */
2883
+ currentDate = input(new Date(), ...(ngDevMode ? [{ debugName: "currentDate" }] : []));
2884
+ /**
2885
+ * Computed calendar grid for the current month.
2886
+ * Automatically updates when currentDate changes.
2887
+ * Returns an array of weeks, each containing 7 days.
2888
+ */
2889
+ weeks = computed(() => {
2890
+ console.log('weeks cambiaron');
2891
+ return getMonthCalendarGrid(this.currentDate());
2892
+ }, ...(ngDevMode ? [{ debugName: "weeks" }] : []));
2893
+ /**
2894
+ * Dynamic grid-template-rows based on actual number of weeks.
2895
+ * Uses minmax(0, auto) to allow expanded weeks to grow beyond equal distribution.
2896
+ */
2897
+ gridTemplateRows = computed(() => `repeat(${this.weeks().length}, minmax(0, auto))`, ...(ngDevMode ? [{ debugName: "gridTemplateRows" }] : []));
2898
+ /**
2899
+ * Calculates which weeks have content that exceeds minimum height.
2900
+ * Returns a Map of weekIndex -> hasOverflow boolean.
2901
+ */
2902
+ weekOverflowMap = computed(() => {
2903
+ const weeks = this.weeks();
2904
+ const events = this.store.currentViewEvents();
2905
+ const minHeight = this.store.minWeekRowHeight();
2906
+ const result = new Map();
2907
+ weeks.forEach((week, index) => {
2908
+ const weekDays = week.days;
2909
+ if (weekDays.length === 0) {
2910
+ result.set(index, false);
2911
+ return;
2912
+ }
2913
+ const weekRange = {
2914
+ start: startOfDay(weekDays[0].date),
2915
+ end: endOfDay(weekDays[weekDays.length - 1].date)
2916
+ };
2917
+ const weekEvents = events.filter(e => isEventInRange(e, weekRange));
2918
+ if (weekEvents.length === 0) {
2919
+ result.set(index, false);
2920
+ return;
2921
+ }
2922
+ const slots = sliceEventsByWeek(weekEvents, weekRange);
2923
+ const rowHeight = SLOT_HEIGHT + SLOT_GAP;
2924
+ const maxRow = Math.max(...slots.map(slot => Math.floor(slot.position.top / rowHeight)));
2925
+ const rows = maxRow + 1;
2926
+ const expandedHeight = CELL_HEADER_HEIGHT + (rows * SLOT_HEIGHT) + ((rows - 1) * SLOT_GAP) + SLOT_GAP;
2927
+ result.set(index, expandedHeight > minHeight);
2928
+ });
2929
+ return result;
2930
+ }, ...(ngDevMode ? [{ debugName: "weekOverflowMap" }] : []));
2931
+ /**
2932
+ * Checks if a week has overflowing content.
2933
+ * @param weekIndex - Index of the week to check
2934
+ * @returns true if the week has more events than can fit
2935
+ */
2936
+ hasOverflow(weekIndex) {
2937
+ return this.weekOverflowMap().get(weekIndex) ?? false;
2938
+ }
2939
+ /**
2940
+ * Implements Selectable.getDateFromPoint()
2941
+ *
2942
+ * Translates visual coordinates (relative to the grid container) into a calendar date.
2943
+ * This enables the SelectableDirective to determine which date the user is selecting.
2944
+ *
2945
+ * Algorithm:
2946
+ * 1. Convert relative coordinates to absolute viewport coordinates
2947
+ * 2. Use document.elementFromPoint to find the cell element under the cursor
2948
+ * 3. Extract the date from the cell's data-date attribute
2949
+ * 4. Find and return the corresponding CalendarDay object
2950
+ *
2951
+ * @param x - X coordinate relative to the grid container
2952
+ * @param y - Y coordinate relative to the grid container
2953
+ * @returns Object with date property, or null if no date found at coordinates
2954
+ */
2955
+ getDateFromPoint(x, y) {
2956
+ // Convert relative coordinates to absolute viewport coordinates
2957
+ const absoluteCoords = this.getAbsoluteCoordinates(x, y);
2958
+ if (!absoluteCoords)
2959
+ return null;
2960
+ // Find the calendar cell element at the cursor position
2961
+ const cellElement = this.findCellElementAtPoint(absoluteCoords.x, absoluteCoords.y);
2962
+ if (!cellElement)
2963
+ return null;
2964
+ // Extract and parse the date from the cell
2965
+ const date = this.extractDateFromCell(cellElement);
2966
+ if (!date)
2967
+ return null;
2968
+ return { date };
2969
+ }
2970
+ /**
2971
+ * Converts coordinates relative to the grid to absolute viewport coordinates
2972
+ *
2973
+ * @param relativeX - X coordinate relative to grid container
2974
+ * @param relativeY - Y coordinate relative to grid container
2975
+ * @returns Absolute coordinates or null if grid element not found
2976
+ */
2977
+ getAbsoluteCoordinates(relativeX, relativeY) {
2978
+ const gridElement = this.elementRef.nativeElement;
2979
+ if (!gridElement)
2980
+ return null;
2981
+ const gridRect = gridElement.getBoundingClientRect();
2982
+ return {
2983
+ x: gridRect.left + relativeX,
2984
+ y: gridRect.top + relativeY
2985
+ };
2986
+ }
2987
+ /**
2988
+ * Finds the month cell element at the given viewport coordinates
2989
+ *
2990
+ * @param absoluteX - Absolute X coordinate in viewport
2991
+ * @param absoluteY - Absolute Y coordinate in viewport
2992
+ * @returns The mglon-month-cell element or null if not found
2993
+ */
2994
+ findCellElementAtPoint(absoluteX, absoluteY) {
2995
+ const element = document.elementFromPoint(absoluteX, absoluteY);
2996
+ if (!element)
2997
+ return null;
2998
+ // Walk up the DOM tree to find the month-cell element
2999
+ return element.closest('mglon-month-cell');
3000
+ }
3001
+ /**
3002
+ * Extracts the date from a month cell element's data attribute
3003
+ *
3004
+ * @param cellElement - The mglon-month-cell element
3005
+ * @returns The date object or null if date attribute is missing/invalid
3006
+ */
3007
+ extractDateFromCell(cellElement) {
3008
+ const dateStr = cellElement.getAttribute('data-date');
3009
+ if (!dateStr)
3010
+ return null;
3011
+ return new Date(dateStr);
3012
+ }
3013
+ /**
3014
+ * Handles resize events from the first week container.
3015
+ * Stores the height in CalendarStore to be shared with all weeks.
3016
+ */
3017
+ onWeekResize(event) {
3018
+ console.log('week resize', event);
3019
+ this.store.setWeekRowHeight(event.height);
3020
+ }
3021
+ /**
3022
+ * Toggles the expansion state of a week.
3023
+ * @param weekIndex - Index of the week to toggle (0-based)
3024
+ */
3025
+ onToggleWeek(weekIndex) {
3026
+ this.store.toggleWeekExpansion(weekIndex);
3027
+ }
3028
+ /**
3029
+ * Checks if a week is currently expanded.
3030
+ * @param weekIndex - Index of the week to check
3031
+ * @returns true if the week is expanded
3032
+ */
3033
+ isWeekExpanded(weekIndex) {
3034
+ return this.store.expandedWeekIndex() === weekIndex;
3035
+ }
3036
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthGrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
3037
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: MonthGrid, isStandalone: true, selector: "mglon-month-grid", inputs: { currentDate: { classPropertyName: "currentDate", publicName: "currentDate", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "selectableDirective", first: true, predicate: SelectableDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"mglon-month-grid\" [mglonSelectable]=\"this\" [style.grid-template-rows]=\"gridTemplateRows()\">\n @for (week of weeks(); track $index) {\n <!-- Toggle button cell (only show button if week has overflow) -->\n <div class=\"mglon-month-grid__toggle\">\n @if (hasOverflow($index)) {\n <button mglon-icon-button size=\"xs\" [rounded]=\"'full'\" class=\"mglon-month-grid__toggle-btn\"\n [class.mglon-month-grid__toggle-btn--expanded]=\"isWeekExpanded($index)\" (click)=\"onToggleWeek($index)\">\n <mglon-icon name=\"chevron-right\"></mglon-icon>\n </button>\n }\n </div>\n\n <!-- Week content -->\n @if ($first) {\n <mglon-month-week [week]=\"week\" [weekIndex]=\"$index\" [expanded]=\"isWeekExpanded($index)\"\n (mglonResizeObserver)=\"onWeekResize($event)\">\n </mglon-month-week>\n } @else {\n <mglon-month-week [week]=\"week\" [weekIndex]=\"$index\" [expanded]=\"isWeekExpanded($index)\">\n </mglon-month-week>\n }\n }\n\n <!-- Selection overlay -->\n @if (selectableDirective()) {\n <mglon-selection [selection]=\"selectableDirective()!.selection()\"\n [isSelecting]=\"selectableDirective()!.isSelecting()\">\n </mglon-selection>\n }\n</div>", styles: [":host{display:block;height:100%;width:100%}.mglon-month-grid{display:grid;grid-template-columns:32px 1fr;width:100%;height:100%;-webkit-user-select:none;user-select:none;cursor:auto}.mglon-month-grid__toggle{position:sticky;left:0;z-index:var(--mglon-schedule-z-sticky, 10);background-color:var(--mglon-schedule-surface, #fff);display:flex;flex-direction:column}.mglon-month-grid__toggle-btn{flex:0 0 auto;display:flex;align-items:flex-start;justify-content:center;padding-top:var(--mglon-schedule-padding-xs, 4px);color:var(--mglon-schedule-text-secondary);transition:color var(--mglon-schedule-transition-duration, .2s) var(--mglon-schedule-transition-easing, ease)}.mglon-month-grid__toggle-btn mglon-icon{transition:transform var(--mglon-schedule-transition-duration, .2s) var(--mglon-schedule-transition-easing, ease)}.mglon-month-grid__toggle-btn:hover{color:var(--mglon-schedule-text-primary)}.mglon-month-grid__toggle-btn--expanded mglon-icon{transform:rotate(90deg)}\n"], dependencies: [{ kind: "component", type: Selection, selector: "mglon-selection", inputs: ["selection", "isSelecting"] }, { kind: "directive", type: SelectableDirective, selector: "[mglonSelectable]", inputs: ["mglonSelectable"], outputs: ["selectionStart", "selectionChange", "selectionEnd"] }, { kind: "component", type: MonthWeek, selector: "mglon-month-week", inputs: ["week", "weekIndex", "expanded"] }, { kind: "directive", type: ResizeObserverDirective, selector: "[mglonResizeObserver]", outputs: ["mglonResizeObserver"] }, { kind: "component", type: IconButtonComponent, selector: "button[mglon-icon-button], a[mglon-icon-button]", inputs: ["appereance", "color", "size", "rounded", "active", "disabled"] }, { kind: "component", type: IconComponent, selector: "mglon-icon", inputs: ["name"] }] });
3038
+ }
3039
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthGrid, decorators: [{
3040
+ type: Component,
3041
+ args: [{ selector: 'mglon-month-grid', imports: [Selection, SelectableDirective, MonthWeek, ResizeObserverDirective, IconButtonComponent, IconComponent], template: "<div class=\"mglon-month-grid\" [mglonSelectable]=\"this\" [style.grid-template-rows]=\"gridTemplateRows()\">\n @for (week of weeks(); track $index) {\n <!-- Toggle button cell (only show button if week has overflow) -->\n <div class=\"mglon-month-grid__toggle\">\n @if (hasOverflow($index)) {\n <button mglon-icon-button size=\"xs\" [rounded]=\"'full'\" class=\"mglon-month-grid__toggle-btn\"\n [class.mglon-month-grid__toggle-btn--expanded]=\"isWeekExpanded($index)\" (click)=\"onToggleWeek($index)\">\n <mglon-icon name=\"chevron-right\"></mglon-icon>\n </button>\n }\n </div>\n\n <!-- Week content -->\n @if ($first) {\n <mglon-month-week [week]=\"week\" [weekIndex]=\"$index\" [expanded]=\"isWeekExpanded($index)\"\n (mglonResizeObserver)=\"onWeekResize($event)\">\n </mglon-month-week>\n } @else {\n <mglon-month-week [week]=\"week\" [weekIndex]=\"$index\" [expanded]=\"isWeekExpanded($index)\">\n </mglon-month-week>\n }\n }\n\n <!-- Selection overlay -->\n @if (selectableDirective()) {\n <mglon-selection [selection]=\"selectableDirective()!.selection()\"\n [isSelecting]=\"selectableDirective()!.isSelecting()\">\n </mglon-selection>\n }\n</div>", styles: [":host{display:block;height:100%;width:100%}.mglon-month-grid{display:grid;grid-template-columns:32px 1fr;width:100%;height:100%;-webkit-user-select:none;user-select:none;cursor:auto}.mglon-month-grid__toggle{position:sticky;left:0;z-index:var(--mglon-schedule-z-sticky, 10);background-color:var(--mglon-schedule-surface, #fff);display:flex;flex-direction:column}.mglon-month-grid__toggle-btn{flex:0 0 auto;display:flex;align-items:flex-start;justify-content:center;padding-top:var(--mglon-schedule-padding-xs, 4px);color:var(--mglon-schedule-text-secondary);transition:color var(--mglon-schedule-transition-duration, .2s) var(--mglon-schedule-transition-easing, ease)}.mglon-month-grid__toggle-btn mglon-icon{transition:transform var(--mglon-schedule-transition-duration, .2s) var(--mglon-schedule-transition-easing, ease)}.mglon-month-grid__toggle-btn:hover{color:var(--mglon-schedule-text-primary)}.mglon-month-grid__toggle-btn--expanded mglon-icon{transform:rotate(90deg)}\n"] }]
3042
+ }], propDecorators: { selectableDirective: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SelectableDirective), { isSignal: true }] }], currentDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentDate", required: false }] }] } });
3043
+
3044
+ class MonthView {
3045
+ calendarStore = inject(CalendarStore);
3046
+ // Get month grid element reference
3047
+ gridElement = viewChild('gridRef', ...(ngDevMode ? [{ debugName: "gridElement" }] : []));
3048
+ constructor() {
3049
+ // Set up grid synchronization after view init
3050
+ afterNextRender(() => {
3051
+ // Logic for grid sizing can remain if needed for the grid itself
3052
+ });
3053
+ }
3054
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthView, deps: [], target: i0.ɵɵFactoryTarget.Component });
3055
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.6", type: MonthView, isStandalone: true, selector: "mglon-month-view", viewQueries: [{ propertyName: "gridElement", first: true, predicate: ["gridRef"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"mglon-month-view\">\n <!-- Single scrollable container for header and grid -->\n <div class=\"mglon-month-view__content\">\n <!-- Header wrapper -->\n <div class=\"mglon-month-view__header-wrapper\">\n <mglon-month-header></mglon-month-header>\n </div>\n\n <!-- Grid container with reference -->\n <div class=\"mglon-month-view__grid-container\" #gridRef>\n <mglon-month-grid [currentDate]=\"calendarStore.currentDate()\"></mglon-month-grid>\n </div>\n\n </div>\n</div>", styles: [":host{display:block;height:100%}.mglon-month-view{height:100%}.mglon-month-view__content{display:grid;grid-template-rows:auto 1fr;height:100%;overflow-x:auto;overflow-y:auto;scrollbar-gutter:stable;min-width:0}.mglon-month-view__header-wrapper{position:sticky;top:0;z-index:var(--mglon-schedule-z-sticky, 10);background-color:var(--mglon-schedule-surface, #fff)}.mglon-month-view__header-wrapper>mglon-month-header{min-width:700px}.mglon-month-view__grid-container{position:relative;min-width:0}.mglon-month-view__grid-container>mglon-month-grid{min-width:700px}.mglon-month-view__events-layer{position:absolute;inset:0;pointer-events:none}.mglon-month-view__events-layer>*{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: MonthHeader, selector: "mglon-month-header" }, { kind: "component", type: MonthGrid, selector: "mglon-month-grid", inputs: ["currentDate"] }] });
3056
+ }
3057
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MonthView, decorators: [{
3058
+ type: Component,
3059
+ args: [{ selector: 'mglon-month-view', standalone: true, imports: [CommonModule, MonthHeader, MonthGrid], template: "<div class=\"mglon-month-view\">\n <!-- Single scrollable container for header and grid -->\n <div class=\"mglon-month-view__content\">\n <!-- Header wrapper -->\n <div class=\"mglon-month-view__header-wrapper\">\n <mglon-month-header></mglon-month-header>\n </div>\n\n <!-- Grid container with reference -->\n <div class=\"mglon-month-view__grid-container\" #gridRef>\n <mglon-month-grid [currentDate]=\"calendarStore.currentDate()\"></mglon-month-grid>\n </div>\n\n </div>\n</div>", styles: [":host{display:block;height:100%}.mglon-month-view{height:100%}.mglon-month-view__content{display:grid;grid-template-rows:auto 1fr;height:100%;overflow-x:auto;overflow-y:auto;scrollbar-gutter:stable;min-width:0}.mglon-month-view__header-wrapper{position:sticky;top:0;z-index:var(--mglon-schedule-z-sticky, 10);background-color:var(--mglon-schedule-surface, #fff)}.mglon-month-view__header-wrapper>mglon-month-header{min-width:700px}.mglon-month-view__grid-container{position:relative;min-width:0}.mglon-month-view__grid-container>mglon-month-grid{min-width:700px}.mglon-month-view__events-layer{position:absolute;inset:0;pointer-events:none}.mglon-month-view__events-layer>*{pointer-events:auto}\n"] }]
3060
+ }], ctorParameters: () => [], propDecorators: { gridElement: [{ type: i0.ViewChild, args: ['gridRef', { isSignal: true }] }] } });
3061
+
3062
+ class Avatar {
3063
+ /** Image source URL (optional) */
3064
+ src = input('', ...(ngDevMode ? [{ debugName: "src" }] : []));
3065
+ /** Display name to derive initials from (optional) */
3066
+ name = input('', ...(ngDevMode ? [{ debugName: "name" }] : []));
3067
+ /** Background color for initials placeholder (optional) */
3068
+ color = input('', ...(ngDevMode ? [{ debugName: "color" }] : []));
3069
+ /** Alt text for accessibility */
3070
+ alt = input('Avatar', ...(ngDevMode ? [{ debugName: "alt" }] : []));
3071
+ /** Size variant: xs (24px), sm (32px), md (40px), lg (48px), xl (64px) */
3072
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
3073
+ /** Show border around avatar */
3074
+ showBorder = input(false, ...(ngDevMode ? [{ debugName: "showBorder" }] : []));
3075
+ /** Border color (CSS color value) */
3076
+ borderColor = input('var(--ngx-cal-primary)', ...(ngDevMode ? [{ debugName: "borderColor" }] : []));
3077
+ /** Inactive state - applies grayscale and hides border */
3078
+ inactive = input(false, ...(ngDevMode ? [{ debugName: "inactive" }] : []));
3079
+ /** Track if image failed to load */
3080
+ imageError = false;
3081
+ /** Computed initials from the name */
3082
+ initials = computed(() => {
3083
+ const nameStr = this.name();
3084
+ if (!nameStr)
3085
+ return '';
3086
+ const parts = nameStr.trim().split(/\s+/);
3087
+ if (parts.length === 0)
3088
+ return '';
3089
+ if (parts.length === 1) {
3090
+ const p = parts[0];
3091
+ return p.length > 1 ? p.substring(0, 2).toUpperCase() : p.toUpperCase();
3092
+ }
3093
+ return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
3094
+ }, ...(ngDevMode ? [{ debugName: "initials" }] : []));
3095
+ /** Computed style for host (border and gap) */
3096
+ hostStyle = computed(() => {
3097
+ const styles = {};
3098
+ if (!this.inactive() && this.showBorder()) {
3099
+ styles.border = `2px solid ${this.borderColor()}`;
3100
+ styles.padding = '2px'; // Gap between border and circle
3101
+ styles.borderRadius = '50%'; // Ensure border is circular
3102
+ }
3103
+ return styles;
3104
+ }, ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
3105
+ /** Computed style for inner element (background and text color) */
3106
+ innerStyle = computed(() => {
3107
+ const styles = {};
3108
+ // Background color logic
3109
+ const bgColor = this.color();
3110
+ if (bgColor) {
3111
+ // Use slightly darker variant for background as requested
3112
+ const darkBg = getHoverColor(bgColor);
3113
+ styles.backgroundColor = darkBg;
3114
+ styles.color = getTextColor(darkBg);
3115
+ }
3116
+ return styles;
3117
+ }, ...(ngDevMode ? [{ debugName: "innerStyle" }] : []));
3118
+ onImageError() {
3119
+ this.imageError = true;
3120
+ }
3121
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Avatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
3122
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: Avatar, isStandalone: true, selector: "mglon-avatar", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showBorder: { classPropertyName: "showBorder", publicName: "showBorder", isSignal: true, isRequired: false, transformFunction: null }, borderColor: { classPropertyName: "borderColor", publicName: "borderColor", isSignal: true, isRequired: false, transformFunction: null }, inactive: { classPropertyName: "inactive", publicName: "inactive", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-size": "size()", "attr.data-inactive": "inactive()", "style": "hostStyle()" } }, ngImport: i0, template: "@if (src() && !imageError) {\n<img [src]=\"src()\" [alt]=\"alt()\" [ngStyle]=\"innerStyle()\" (error)=\"onImageError()\" class=\"avatar-image\" />\n} @else if (initials()) {\n<div class=\"avatar-initials\" [ngStyle]=\"innerStyle()\">\n {{ initials() }}\n</div>\n} @else {\n<div class=\"avatar-placeholder\" [ngStyle]=\"innerStyle()\">\n <span class=\"avatar-icon\">\uD83D\uDC64</span>\n</div>\n}", styles: [":host{display:inline-block;border-radius:50%;box-sizing:border-box}:host[data-size=xs]{width:24px;height:24px}:host[data-size=xs] .avatar-icon,:host[data-size=xs] .avatar-initials{font-size:10px}:host[data-size=sm]{width:32px;height:32px}:host[data-size=sm] .avatar-icon,:host[data-size=sm] .avatar-initials{font-size:12px}:host[data-size=md]{width:40px;height:40px}:host[data-size=md] .avatar-icon,:host[data-size=md] .avatar-initials{font-size:14px}:host[data-size=lg]{width:48px;height:48px}:host[data-size=lg] .avatar-icon,:host[data-size=lg] .avatar-initials{font-size:16px}:host[data-size=xl]{width:64px;height:64px}:host[data-size=xl] .avatar-icon,:host[data-size=xl] .avatar-initials{font-size:20px}:host[data-inactive=true] .avatar-image,:host[data-inactive=true] .avatar-placeholder,:host[data-inactive=true] .avatar-initials{filter:grayscale(100%);opacity:.6}.avatar-image,.avatar-placeholder,.avatar-initials{width:100%;height:100%;border-radius:50%;object-fit:cover;display:block}.avatar-placeholder,.avatar-initials{background-color:var(--mglon-schedule-surface, #f0f0f0);display:flex;align-items:center;justify-content:center;color:var(--mglon-schedule-text-secondary, #666);-webkit-user-select:none;user-select:none;font-weight:600;text-transform:uppercase}.avatar-icon{line-height:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
3123
+ }
3124
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Avatar, decorators: [{
3125
+ type: Component,
3126
+ args: [{ selector: 'mglon-avatar', standalone: true, imports: [CommonModule], host: {
3127
+ '[attr.data-size]': 'size()',
3128
+ '[attr.data-inactive]': 'inactive()',
3129
+ '[style]': 'hostStyle()'
3130
+ }, template: "@if (src() && !imageError) {\n<img [src]=\"src()\" [alt]=\"alt()\" [ngStyle]=\"innerStyle()\" (error)=\"onImageError()\" class=\"avatar-image\" />\n} @else if (initials()) {\n<div class=\"avatar-initials\" [ngStyle]=\"innerStyle()\">\n {{ initials() }}\n</div>\n} @else {\n<div class=\"avatar-placeholder\" [ngStyle]=\"innerStyle()\">\n <span class=\"avatar-icon\">\uD83D\uDC64</span>\n</div>\n}", styles: [":host{display:inline-block;border-radius:50%;box-sizing:border-box}:host[data-size=xs]{width:24px;height:24px}:host[data-size=xs] .avatar-icon,:host[data-size=xs] .avatar-initials{font-size:10px}:host[data-size=sm]{width:32px;height:32px}:host[data-size=sm] .avatar-icon,:host[data-size=sm] .avatar-initials{font-size:12px}:host[data-size=md]{width:40px;height:40px}:host[data-size=md] .avatar-icon,:host[data-size=md] .avatar-initials{font-size:14px}:host[data-size=lg]{width:48px;height:48px}:host[data-size=lg] .avatar-icon,:host[data-size=lg] .avatar-initials{font-size:16px}:host[data-size=xl]{width:64px;height:64px}:host[data-size=xl] .avatar-icon,:host[data-size=xl] .avatar-initials{font-size:20px}:host[data-inactive=true] .avatar-image,:host[data-inactive=true] .avatar-placeholder,:host[data-inactive=true] .avatar-initials{filter:grayscale(100%);opacity:.6}.avatar-image,.avatar-placeholder,.avatar-initials{width:100%;height:100%;border-radius:50%;object-fit:cover;display:block}.avatar-placeholder,.avatar-initials{background-color:var(--mglon-schedule-surface, #f0f0f0);display:flex;align-items:center;justify-content:center;color:var(--mglon-schedule-text-secondary, #666);-webkit-user-select:none;user-select:none;font-weight:600;text-transform:uppercase}.avatar-icon{line-height:1}\n"] }]
3131
+ }], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showBorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBorder", required: false }] }], borderColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderColor", required: false }] }], inactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "inactive", required: false }] }] } });
3132
+
3133
+ class ResourceItemSchedule {
3134
+ /** Resource to display */
3135
+ resource = input.required(...(ngDevMode ? [{ debugName: "resource" }] : []));
3136
+ /** Border radius for resource item */
3137
+ rounded = input('sm', ...(ngDevMode ? [{ debugName: "rounded" }] : []));
3138
+ /** Density/spacing for resource item */
3139
+ density = input('comfortable', ...(ngDevMode ? [{ debugName: "density" }] : []));
3140
+ /** Emitted when the resource is clicked (for toggle) */
3141
+ toggle = output();
3142
+ onToggle() {
3143
+ this.toggle.emit(this.resource().id);
3144
+ }
3145
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResourceItemSchedule, deps: [], target: i0.ɵɵFactoryTarget.Component });
3146
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: ResourceItemSchedule, isStandalone: true, selector: "mglon-resource-item-schedule", inputs: { resource: { classPropertyName: "resource", publicName: "resource", isSignal: true, isRequired: true, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggle: "toggle" }, host: { properties: { "attr.rounded": "rounded()", "attr.density": "density()" } }, ngImport: i0, template: "<div class=\"resource-item\" (click)=\"onToggle()\">\n <mglon-avatar [src]=\"resource().avatar || ''\" [name]=\"resource().name\" [color]=\"resource().color || ''\"\n [alt]=\"resource().name\" size=\"xs\" [showBorder]=\"true\" [borderColor]=\"resource().color || 'var(--ngx-cal-primary)'\"\n [inactive]=\"resource().isActive === false\" />\n <span class=\"resource-name\" [class.inactive]=\"resource().isActive === false\">{{ resource().name }}</span>\n</div>", styles: [".resource-item{display:flex;align-items:center;gap:var(--mglon-resource-item-gap, var(--mglon-schedule-gap-sm));padding:var(--mglon-resource-item-padding-y, var(--mglon-schedule-padding-sm)) var(--mglon-resource-item-padding-x, var(--mglon-schedule-padding-md));cursor:pointer;transition:background-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing);border-radius:var(--mglon-resource-item-radius, var(--mglon-schedule-radius-sm))}.resource-item:hover{background-color:var(--mglon-resource-item-hover-bg, var(--mglon-schedule-hover-bg))}.resource-item:active{background-color:var(--mglon-resource-item-active-bg, rgba(0, 0, 0, .08))}.resource-name{font-size:var(--mglon-resource-item-font-size, var(--mglon-schedule-font-size-sm));color:var(--mglon-resource-item-text-color, var(--mglon-schedule-text-primary));font-weight:400;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none}.resource-name.inactive{color:var(--mglon-resource-item-inactive-color, var(--mglon-schedule-text-secondary));opacity:var(--mglon-resource-item-inactive-opacity, .6)}:host([rounded=none]) .resource-item{border-radius:0}:host([rounded=sm]) .resource-item{border-radius:var(--mglon-schedule-radius-sm)}:host([rounded=md]) .resource-item{border-radius:var(--mglon-schedule-radius-md)}:host([rounded=lg]) .resource-item{border-radius:var(--mglon-schedule-radius-lg)}:host([rounded=full]) .resource-item{border-radius:var(--mglon-schedule-radius-full)}:host([density=compact]) .resource-item{padding:var(--mglon-resource-item-padding-compact-y, var(--mglon-schedule-padding-xs)) var(--mglon-resource-item-padding-compact-x, var(--mglon-schedule-padding-sm));gap:var(--mglon-resource-item-gap-compact, var(--mglon-schedule-gap-xs))}:host([density=comfortable]) .resource-item{padding:var(--mglon-resource-item-padding-comfortable-y, var(--mglon-schedule-padding-sm)) var(--mglon-resource-item-padding-comfortable-x, var(--mglon-schedule-padding-md));gap:var(--mglon-resource-item-gap-comfortable, var(--mglon-schedule-gap-sm))}\n"], dependencies: [{ kind: "component", type: Avatar, selector: "mglon-avatar", inputs: ["src", "name", "color", "alt", "size", "showBorder", "borderColor", "inactive"] }] });
3147
+ }
3148
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResourceItemSchedule, decorators: [{
3149
+ type: Component,
3150
+ args: [{ selector: 'mglon-resource-item-schedule', standalone: true, imports: [Avatar], host: {
3151
+ '[attr.rounded]': 'rounded()',
3152
+ '[attr.density]': 'density()'
3153
+ }, template: "<div class=\"resource-item\" (click)=\"onToggle()\">\n <mglon-avatar [src]=\"resource().avatar || ''\" [name]=\"resource().name\" [color]=\"resource().color || ''\"\n [alt]=\"resource().name\" size=\"xs\" [showBorder]=\"true\" [borderColor]=\"resource().color || 'var(--ngx-cal-primary)'\"\n [inactive]=\"resource().isActive === false\" />\n <span class=\"resource-name\" [class.inactive]=\"resource().isActive === false\">{{ resource().name }}</span>\n</div>", styles: [".resource-item{display:flex;align-items:center;gap:var(--mglon-resource-item-gap, var(--mglon-schedule-gap-sm));padding:var(--mglon-resource-item-padding-y, var(--mglon-schedule-padding-sm)) var(--mglon-resource-item-padding-x, var(--mglon-schedule-padding-md));cursor:pointer;transition:background-color var(--mglon-schedule-transition-duration) var(--mglon-schedule-transition-easing);border-radius:var(--mglon-resource-item-radius, var(--mglon-schedule-radius-sm))}.resource-item:hover{background-color:var(--mglon-resource-item-hover-bg, var(--mglon-schedule-hover-bg))}.resource-item:active{background-color:var(--mglon-resource-item-active-bg, rgba(0, 0, 0, .08))}.resource-name{font-size:var(--mglon-resource-item-font-size, var(--mglon-schedule-font-size-sm));color:var(--mglon-resource-item-text-color, var(--mglon-schedule-text-primary));font-weight:400;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none}.resource-name.inactive{color:var(--mglon-resource-item-inactive-color, var(--mglon-schedule-text-secondary));opacity:var(--mglon-resource-item-inactive-opacity, .6)}:host([rounded=none]) .resource-item{border-radius:0}:host([rounded=sm]) .resource-item{border-radius:var(--mglon-schedule-radius-sm)}:host([rounded=md]) .resource-item{border-radius:var(--mglon-schedule-radius-md)}:host([rounded=lg]) .resource-item{border-radius:var(--mglon-schedule-radius-lg)}:host([rounded=full]) .resource-item{border-radius:var(--mglon-schedule-radius-full)}:host([density=compact]) .resource-item{padding:var(--mglon-resource-item-padding-compact-y, var(--mglon-schedule-padding-xs)) var(--mglon-resource-item-padding-compact-x, var(--mglon-schedule-padding-sm));gap:var(--mglon-resource-item-gap-compact, var(--mglon-schedule-gap-xs))}:host([density=comfortable]) .resource-item{padding:var(--mglon-resource-item-padding-comfortable-y, var(--mglon-schedule-padding-sm)) var(--mglon-resource-item-padding-comfortable-x, var(--mglon-schedule-padding-md));gap:var(--mglon-resource-item-gap-comfortable, var(--mglon-schedule-gap-sm))}\n"] }]
3154
+ }], propDecorators: { resource: [{ type: i0.Input, args: [{ isSignal: true, alias: "resource", required: true }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], toggle: [{ type: i0.Output, args: ["toggle"] }] } });
3155
+
3156
+ class ResourceListSchedule {
3157
+ calendarStore = inject(CalendarStore);
3158
+ // Sidebar UI Configuration from Store
3159
+ resourceItemRounded = computed(() => this.calendarStore.uiConfig().sidebar.resourceItems.rounded, ...(ngDevMode ? [{ debugName: "resourceItemRounded" }] : []));
3160
+ resourceItemDensity = computed(() => this.calendarStore.uiConfig().sidebar.resourceItems.density, ...(ngDevMode ? [{ debugName: "resourceItemDensity" }] : []));
3161
+ /** Array of resources to display */
3162
+ resources = input([], ...(ngDevMode ? [{ debugName: "resources" }] : []));
3163
+ /** Emitted when a resource is toggled */
3164
+ toggleResource = output();
3165
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResourceListSchedule, deps: [], target: i0.ɵɵFactoryTarget.Component });
3166
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ResourceListSchedule, isStandalone: true, selector: "mglon-resource-list-schedule", inputs: { resources: { classPropertyName: "resources", publicName: "resources", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleResource: "toggleResource" }, ngImport: i0, template: "<div class=\"resource-list\">\n @if (resources().length > 0) {\n @for (resource of resources(); track resource.id) {\n <mglon-resource-item-schedule [resource]=\"resource\" [rounded]=\"resourceItemRounded()\"\n [density]=\"resourceItemDensity()\" (toggle)=\"toggleResource.emit($event)\" />\n }\n } @else {\n <div class=\"resource-list-empty\">\n <span class=\"empty-icon\">\uD83D\uDCCB</span>\n <span class=\"empty-text\">No resources available</span>\n </div>\n }\n</div>", styles: [".resource-list{display:flex;flex-direction:column;gap:var(--mglon-resource-list-gap, var(--mglon-schedule-gap-xs));overflow-y:auto;max-height:100%}.resource-list-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--mglon-resource-list-empty-padding, var(--mglon-schedule-padding-lg));gap:var(--mglon-resource-list-empty-gap, var(--mglon-schedule-gap-sm));color:var(--mglon-resource-list-empty-color, var(--mglon-schedule-text-secondary));text-align:center}.empty-icon{font-size:var(--mglon-resource-list-empty-icon-size, 2rem);opacity:var(--mglon-resource-list-empty-icon-opacity, .3)}.empty-text{font-size:var(--mglon-resource-list-empty-text-size, var(--mglon-schedule-font-size-sm))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ResourceItemSchedule, selector: "mglon-resource-item-schedule", inputs: ["resource", "rounded", "density"], outputs: ["toggle"] }] });
3167
+ }
3168
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResourceListSchedule, decorators: [{
3169
+ type: Component,
3170
+ args: [{ selector: 'mglon-resource-list-schedule', standalone: true, imports: [CommonModule, ResourceItemSchedule], template: "<div class=\"resource-list\">\n @if (resources().length > 0) {\n @for (resource of resources(); track resource.id) {\n <mglon-resource-item-schedule [resource]=\"resource\" [rounded]=\"resourceItemRounded()\"\n [density]=\"resourceItemDensity()\" (toggle)=\"toggleResource.emit($event)\" />\n }\n } @else {\n <div class=\"resource-list-empty\">\n <span class=\"empty-icon\">\uD83D\uDCCB</span>\n <span class=\"empty-text\">No resources available</span>\n </div>\n }\n</div>", styles: [".resource-list{display:flex;flex-direction:column;gap:var(--mglon-resource-list-gap, var(--mglon-schedule-gap-xs));overflow-y:auto;max-height:100%}.resource-list-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--mglon-resource-list-empty-padding, var(--mglon-schedule-padding-lg));gap:var(--mglon-resource-list-empty-gap, var(--mglon-schedule-gap-sm));color:var(--mglon-resource-list-empty-color, var(--mglon-schedule-text-secondary));text-align:center}.empty-icon{font-size:var(--mglon-resource-list-empty-icon-size, 2rem);opacity:var(--mglon-resource-list-empty-icon-opacity, .3)}.empty-text{font-size:var(--mglon-resource-list-empty-text-size, var(--mglon-schedule-font-size-sm))}\n"] }]
3171
+ }], propDecorators: { resources: [{ type: i0.Input, args: [{ isSignal: true, alias: "resources", required: false }] }], toggleResource: [{ type: i0.Output, args: ["toggleResource"] }] } });
3172
+
3173
+ class SidebarSchedule {
3174
+ store = inject(CalendarStore);
3175
+ backgroundColor = computed(() => this.store.uiConfig().sidebar.background, ...(ngDevMode ? [{ debugName: "backgroundColor" }] : []));
3176
+ borderRadius = computed(() => this.store.uiConfig().sidebar.rounded || 'none', ...(ngDevMode ? [{ debugName: "borderRadius" }] : []));
3177
+ onToggleResource(id) {
3178
+ this.store.toggleResource(id);
3179
+ }
3180
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SidebarSchedule, deps: [], target: i0.ɵɵFactoryTarget.Component });
3181
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: SidebarSchedule, isStandalone: true, selector: "mglon-sidebar-schedule", host: { properties: { "style.--mglon-sidebar-radius": "borderRadius() === \"none\" ? \"0\" : \"var(--mglon-schedule-radius-\" + borderRadius() + \")\"", "style.--mglon-sidebar-bg": "backgroundColor()", "attr.data-radius": "borderRadius()" } }, ngImport: i0, template: "<aside class=\"sidebar-schedule\">\n <header class=\"sidebar-header\">\n <h3 class=\"sidebar-title\">Resources</h3>\n </header>\n <div class=\"sidebar-content\">\n <mglon-resource-list-schedule [resources]=\"store.allResources()\" (toggleResource)=\"onToggleResource($event)\" />\n </div>\n</aside>", styles: [".sidebar-schedule{display:flex;flex-direction:column;height:100%;width:250px;background-color:var(--mglon-sidebar-bg, var(--mglon-schedule-surface));border-right:1px solid var(--mglon-schedule-border);border-radius:var(--mglon-sidebar-radius, var(--mglon-schedule-radius-sm));overflow:hidden}.sidebar-header{padding:var(--mglon-schedule-spacing-md, 12px) var(--mglon-schedule-spacing-md, 12px);background-color:var(--mglon-schedule-background)}.sidebar-title{margin:0;font-size:1rem;font-weight:600;color:var(--mglon-schedule-text-primary)}.sidebar-content{flex:1;overflow-y:auto;padding:var(--mglon-schedule-spacing-sm, 8px)}\n"], dependencies: [{ kind: "component", type: ResourceListSchedule, selector: "mglon-resource-list-schedule", inputs: ["resources"], outputs: ["toggleResource"] }] });
3182
+ }
3183
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SidebarSchedule, decorators: [{
3184
+ type: Component,
3185
+ args: [{ selector: 'mglon-sidebar-schedule', standalone: true, imports: [ResourceListSchedule], host: {
3186
+ '[style.--mglon-sidebar-radius]': 'borderRadius() === "none" ? "0" : "var(--mglon-schedule-radius-" + borderRadius() + ")"',
3187
+ '[style.--mglon-sidebar-bg]': 'backgroundColor()',
3188
+ '[attr.data-radius]': 'borderRadius()'
3189
+ }, template: "<aside class=\"sidebar-schedule\">\n <header class=\"sidebar-header\">\n <h3 class=\"sidebar-title\">Resources</h3>\n </header>\n <div class=\"sidebar-content\">\n <mglon-resource-list-schedule [resources]=\"store.allResources()\" (toggleResource)=\"onToggleResource($event)\" />\n </div>\n</aside>", styles: [".sidebar-schedule{display:flex;flex-direction:column;height:100%;width:250px;background-color:var(--mglon-sidebar-bg, var(--mglon-schedule-surface));border-right:1px solid var(--mglon-schedule-border);border-radius:var(--mglon-sidebar-radius, var(--mglon-schedule-radius-sm));overflow:hidden}.sidebar-header{padding:var(--mglon-schedule-spacing-md, 12px) var(--mglon-schedule-spacing-md, 12px);background-color:var(--mglon-schedule-background)}.sidebar-title{margin:0;font-size:1rem;font-weight:600;color:var(--mglon-schedule-text-primary)}.sidebar-content{flex:1;overflow-y:auto;padding:var(--mglon-schedule-spacing-sm, 8px)}\n"] }]
3190
+ }] });
3191
+
3192
+ class Schedule {
3193
+ config = input({}, ...(ngDevMode ? [{ debugName: "config" }] : []));
3194
+ // ============================================
3195
+ // UI CONFIGURATION INPUTS (Area-Based)
3196
+ // ============================================
3197
+ /** Optional UI configuration for header area (button-group, navigation, today button) */
3198
+ headerUI = input(...(ngDevMode ? [undefined, { debugName: "headerUI" }] : []));
3199
+ /** Optional UI configuration for sidebar area (resource items) */
3200
+ sidebarUI = input(...(ngDevMode ? [undefined, { debugName: "sidebarUI" }] : []));
3201
+ /** Optional UI configuration for grid area (event slots, overflow indicators) */
3202
+ gridUI = input(...(ngDevMode ? [undefined, { debugName: "gridUI" }] : []));
3203
+ /** Optional default color for all events in the schedule */
3204
+ color = input(...(ngDevMode ? [undefined, { debugName: "color" }] : []));
3205
+ /**
3206
+ * Whether to automatically generate vivid/pastel variants for events.
3207
+ * If true, regular events use vivid colors and recurrent events use pastel variants.
3208
+ * If false, colors are used exactly as provided.
3209
+ * @default true
3210
+ */
3211
+ useDynamicColors = input(true, ...(ngDevMode ? [{ debugName: "useDynamicColors" }] : []));
3212
+ add = output();
3213
+ // ============================================
3214
+ // OUTPUT EVENTS
3215
+ // ============================================
3216
+ /** Emitted when selection starts (mousedown) */
3217
+ selectionStart = output();
3218
+ /** Emitted while selecting (mousemove) */
3219
+ selectionChange = output();
3220
+ /** Emitted when selection ends (mouseup) */
3221
+ selectionEnd = output();
3222
+ /** Emitted when view mode changes */
3223
+ viewChange = output();
3224
+ /** Emitted when displayed date range changes */
3225
+ dateRangeChange = output();
3226
+ /** Emitted when a resource is shown/activated */
3227
+ resourceShow = output();
3228
+ /** Emitted when a resource is hidden/deactivated */
3229
+ resourceHide = output();
3230
+ store = inject(CalendarStore);
3231
+ constructor() {
3232
+ // Register global handlers
3233
+ // this.eventStore.setGlobalHandlers({
3234
+ // eventClick: this.eventClick
3235
+ // });
3236
+ effect(() => {
3237
+ const conf = this.config();
3238
+ if (conf) {
3239
+ this.store.updateConfig(conf);
3240
+ }
3241
+ });
3242
+ // Handle UI configuration updates (area-based)
3243
+ effect(() => {
3244
+ const headerConfig = this.headerUI();
3245
+ const sidebarConfig = this.sidebarUI();
3246
+ const gridConfig = this.gridUI();
3247
+ const primaryColor = this.color();
3248
+ const useDynamicColors = this.useDynamicColors();
3249
+ // Only update if at least one config is provided
3250
+ if (headerConfig || sidebarConfig || gridConfig || primaryColor || useDynamicColors !== undefined) {
3251
+ // Construct a safe partial grid config
3252
+ const finalGridConfig = gridConfig ? { ...gridConfig } : {};
3253
+ if (primaryColor) {
3254
+ finalGridConfig.eventSlots = {
3255
+ ...finalGridConfig.eventSlots,
3256
+ color: finalGridConfig.eventSlots?.color || primaryColor
3257
+ };
3258
+ }
3259
+ if (useDynamicColors !== undefined) {
3260
+ finalGridConfig.useDynamicColors = useDynamicColors;
3261
+ }
3262
+ this.store.setUIConfig({
3263
+ header: headerConfig,
3264
+ sidebar: sidebarConfig,
3265
+ grid: finalGridConfig
3266
+ });
3267
+ }
3268
+ });
3269
+ // Emit viewChange when view mode changes
3270
+ effect(() => {
3271
+ const view = this.store.viewMode();
3272
+ this.viewChange.emit(view);
3273
+ });
3274
+ // Emit resource show/hide events when resources change
3275
+ effect(() => {
3276
+ const resources = this.store.allResources();
3277
+ resources.forEach(resource => {
3278
+ // Check previous state vs current state
3279
+ const wasActive = this.previousResourceStates.get(resource.id);
3280
+ const isActive = resource.isActive !== false;
3281
+ if (wasActive !== undefined && wasActive !== isActive) {
3282
+ if (isActive) {
3283
+ this.resourceShow.emit(resource.id);
3284
+ }
3285
+ else {
3286
+ this.resourceHide.emit(resource.id);
3287
+ }
3288
+ }
3289
+ // Update previous state
3290
+ this.previousResourceStates.set(resource.id, isActive);
3291
+ });
3292
+ });
3293
+ }
3294
+ previousResourceStates = new Map();
3295
+ get api() {
3296
+ return {
3297
+ next: () => this.store.next(),
3298
+ prev: () => this.store.prev(),
3299
+ today: () => this.store.today(),
3300
+ changeView: (view) => this.store.changeView(view),
3301
+ setDate: (date) => this.store.setDate(date)
3302
+ };
3303
+ }
3304
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Schedule, deps: [], target: i0.ɵɵFactoryTarget.Component });
3305
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: Schedule, isStandalone: true, selector: "mglon-schedule", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, headerUI: { classPropertyName: "headerUI", publicName: "headerUI", isSignal: true, isRequired: false, transformFunction: null }, sidebarUI: { classPropertyName: "sidebarUI", publicName: "sidebarUI", isSignal: true, isRequired: false, transformFunction: null }, gridUI: { classPropertyName: "gridUI", publicName: "gridUI", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, useDynamicColors: { classPropertyName: "useDynamicColors", publicName: "useDynamicColors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { add: "add", selectionStart: "selectionStart", selectionChange: "selectionChange", selectionEnd: "selectionEnd", viewChange: "viewChange", dateRangeChange: "dateRangeChange", resourceShow: "resourceShow", resourceHide: "resourceHide" }, providers: [CalendarStore], ngImport: i0, template: "<div class=\"ng-sched-schedule\">\n <ng-content />\n <!-- Header -->\n <mglon-header-schedule [title]=\"store.formattedDate()\" [activeView]=\"store.viewMode()\" [views]=\"store.config().views!\"\n [editable]=\"store.config().editable!\" (next)=\"store.next()\" (prev)=\"store.prev()\" (today)=\"store.today()\"\n (viewChange)=\"store.changeView($event)\" (add)=\"add.emit()\">\n </mglon-header-schedule>\n\n <!-- Body -->\n\n <main class=\"ng-sched-body\">\n @if (store.config().showSidebar) {\n <div class=\"ng-sched-body__sidebar\">\n <mglon-sidebar-schedule></mglon-sidebar-schedule>\n </div>\n }\n <div class=\"ng-sched-body__content\">\n @switch (store.viewMode()) {\n @case ('resource') {\n <mglon-resource-view></mglon-resource-view>\n }\n @case ('month') {\n <mglon-month-view></mglon-month-view>\n }\n @case ('week') {\n <mglon-week-view (selectionStart)=\"selectionStart.emit($event)\" (selectionChange)=\"selectionChange.emit($event)\"\n (selectionEnd)=\"selectionEnd.emit($event)\">\n </mglon-week-view>\n }\n @default {\n <div class=\"ng-sched-placeholder\">\n View '{{ store.viewMode() }}' not implemented yet.\n </div>\n }\n }\n </div>\n\n </main>\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;width:100%;--mglon-schedule-primary-50: #e8f0fe;--mglon-schedule-primary-100: #d2e3fc;--mglon-schedule-primary-200: #a7cbfb;--mglon-schedule-primary-300: #7cacf8;--mglon-schedule-primary-400: #518ef5;--mglon-schedule-primary-500: #1a73e8;--mglon-schedule-primary-600: #1967d2;--mglon-schedule-primary-700: #185abc;--mglon-schedule-primary-800: #174ea6;--mglon-schedule-primary-900: #0d3a86;--mglon-schedule-primary-950: #08265a;--mglon-schedule-neutral-50: #fafafa;--mglon-schedule-neutral-100: #f5f5f5;--mglon-schedule-neutral-200: #e5e5e5;--mglon-schedule-neutral-300: #d4d4d4;--mglon-schedule-neutral-400: #a3a3a3;--mglon-schedule-neutral-500: #737373;--mglon-schedule-neutral-600: #525252;--mglon-schedule-neutral-700: #404040;--mglon-schedule-neutral-800: #262626;--mglon-schedule-neutral-900: #171717;--mglon-schedule-neutral-950: #0a0a0a;--mglon-schedule-primary: var(--mglon-schedule-primary-500);--mglon-schedule-on-primary: #ffffff;--mglon-schedule-surface: #ffffff;--mglon-schedule-on-surface: var(--mglon-schedule-neutral-900);--mglon-schedule-surface-variant: var(--mglon-schedule-neutral-100);--mglon-schedule-on-surface-variant: var(--mglon-schedule-neutral-700);--mglon-schedule-background: #ffffff;--mglon-schedule-on-background: var(--mglon-schedule-neutral-900);--mglon-schedule-border: var(--mglon-schedule-neutral-200);--mglon-schedule-text-primary: var(--mglon-schedule-neutral-900);--mglon-schedule-text-secondary: var(--mglon-schedule-neutral-500);--mglon-schedule-text-tertiary: var(--mglon-schedule-neutral-400);--mglon-schedule-hover: var(--mglon-schedule-neutral-100);--mglon-schedule-selection: var(--mglon-schedule-primary-50);--mglon-schedule-error: #ea4335;--mglon-schedule-on-error: #ffffff;--mglon-schedule-spacing-xs: 4px;--mglon-schedule-spacing-sm: 8px;--mglon-schedule-spacing-md: 16px;--mglon-schedule-spacing-lg: 24px;--mglon-schedule-padding-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-padding-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-padding-md: var(--mglon-schedule-spacing-md);--mglon-schedule-padding-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-margin-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-margin-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-margin-md: var(--mglon-schedule-spacing-md);--mglon-schedule-margin-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-gap-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-gap-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-gap-md: var(--mglon-schedule-spacing-md);--mglon-schedule-gap-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-border-width-sm: 1px;--mglon-schedule-border-width-md: 2px;--mglon-schedule-border-width-lg: 4px;--mglon-schedule-header-height: 48px;--mglon-schedule-sidebar-width: 60px;--mglon-schedule-cell-height: 48px;--mglon-schedule-radius-sm: 4px;--mglon-schedule-radius-md: 8px;--mglon-schedule-radius-lg: 16px;--mglon-schedule-radius-full: 9999px;--mglon-schedule-font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;--mglon-schedule-font-size-xs: 10px;--mglon-schedule-font-size-sm: 12px;--mglon-schedule-font-size-md: 14px;--mglon-schedule-font-size-lg: 16px;--mglon-schedule-shadow-sm: 0 1px 2px rgba(0, 0, 0, .1);--mglon-schedule-shadow-md: 0 2px 4px rgba(0, 0, 0, .15);--mglon-schedule-shadow-lg: 0 4px 8px rgba(0, 0, 0, .2);--mglon-schedule-z-sticky: 10;--mglon-schedule-z-header: 20;--mglon-schedule-hover-bg: rgba(0, 0, 0, .04);--mglon-schedule-focus-ring-color: var(--mglon-schedule-primary-200);--mglon-schedule-disabled-opacity: .5;--mglon-schedule-transition-duration: .2s;--mglon-schedule-transition-easing: cubic-bezier(.4, 0, .2, 1);--mglon-schedule-month-week-display: grid;--mglon-schedule-month-week-columns: repeat(7, 1fr);--mglon-schedule-month-week-min-height: 80px;--mglon-schedule-month-week-border-bottom-width: 1px}.ng-sched-schedule{display:grid;grid-template-rows:auto 1fr;height:100%;width:100%;background-color:var(--mglon-schedule-background);color:var(--mglon-schedule-on-background);font-family:var(--mglon-schedule-font-family)}.ng-sched-header{display:flex;align-items:center;justify-content:space-between;padding:var(--mglon-schedule-spacing-sm) var(--mglon-schedule-spacing-md);border-bottom:1px solid var(--mglon-schedule-border);background-color:var(--mglon-schedule-surface)}.ng-sched-header__nav{display:flex;align-items:center;gap:var(--mglon-schedule-spacing-sm)}.ng-sched-header__title{margin:0 0 0 var(--mglon-schedule-spacing-md);font-size:1.25rem;font-weight:500}.ng-sched-header__views{display:flex;gap:0;border:1px solid var(--mglon-schedule-border);border-radius:4px;overflow:hidden}.ng-sched-button{padding:6px 12px;border:1px solid var(--mglon-schedule-border);background-color:transparent;color:var(--mglon-schedule-on-surface);font-family:inherit;font-size:.875rem;cursor:pointer;transition:all .2s ease-in-out;border-radius:4px;outline:none;-webkit-user-select:none;user-select:none}.ng-sched-button--icon{padding:6px 10px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center}.ng-sched-button:hover:not(:disabled){background-color:var(--mglon-schedule-hover)}.ng-sched-button:active:not(:disabled){transform:scale(.98)}.ng-sched-button:focus-visible{outline:2px solid var(--mglon-schedule-primary);outline-offset:2px}.ng-sched-button--active{background-color:var(--mglon-schedule-selection);color:var(--mglon-schedule-primary);font-weight:500}.ng-sched-button:disabled{opacity:.5;cursor:not-allowed}.ng-sched-header__views .ng-sched-button{border:none;border-right:1px solid var(--mglon-schedule-border);border-radius:0}.ng-sched-header__views .ng-sched-button:last-child{border-right:none}.ng-sched-body{position:relative;height:100%;overflow:hidden;display:grid;grid-template-columns:auto 1fr;gap:0}.ng-sched-body:has(>:only-child){grid-template-columns:1fr}.ng-sched-body>:last-child{overflow:auto;min-width:0}.ng-sched-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:var(--mglon-schedule-spacing-lg);color:var(--mglon-schedule-text-secondary);font-size:1rem;text-align:center;gap:var(--mglon-schedule-spacing-md)}.ng-sched-placeholder:before{content:\"\\1f4c5\";font-size:3rem;opacity:.3}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ResourceView, selector: "mglon-resource-view" }, { kind: "component", type: MonthView, selector: "mglon-month-view" }, { kind: "component", type: HeaderSchedule, selector: "mglon-header-schedule", inputs: ["title", "activeView", "views", "editable"], outputs: ["next", "prev", "today", "viewChange", "add"] }, { kind: "component", type: SidebarSchedule, selector: "mglon-sidebar-schedule" }, { kind: "component", type: WeekView, selector: "mglon-week-view", outputs: ["selectionStart", "selectionChange", "selectionEnd"] }], encapsulation: i0.ViewEncapsulation.None });
3306
+ }
3307
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Schedule, decorators: [{
3308
+ type: Component,
3309
+ args: [{ selector: 'mglon-schedule', standalone: true, imports: [CommonModule, ResourceView, MonthView, HeaderSchedule, SidebarSchedule, WeekView], providers: [CalendarStore], encapsulation: ViewEncapsulation.None, template: "<div class=\"ng-sched-schedule\">\n <ng-content />\n <!-- Header -->\n <mglon-header-schedule [title]=\"store.formattedDate()\" [activeView]=\"store.viewMode()\" [views]=\"store.config().views!\"\n [editable]=\"store.config().editable!\" (next)=\"store.next()\" (prev)=\"store.prev()\" (today)=\"store.today()\"\n (viewChange)=\"store.changeView($event)\" (add)=\"add.emit()\">\n </mglon-header-schedule>\n\n <!-- Body -->\n\n <main class=\"ng-sched-body\">\n @if (store.config().showSidebar) {\n <div class=\"ng-sched-body__sidebar\">\n <mglon-sidebar-schedule></mglon-sidebar-schedule>\n </div>\n }\n <div class=\"ng-sched-body__content\">\n @switch (store.viewMode()) {\n @case ('resource') {\n <mglon-resource-view></mglon-resource-view>\n }\n @case ('month') {\n <mglon-month-view></mglon-month-view>\n }\n @case ('week') {\n <mglon-week-view (selectionStart)=\"selectionStart.emit($event)\" (selectionChange)=\"selectionChange.emit($event)\"\n (selectionEnd)=\"selectionEnd.emit($event)\">\n </mglon-week-view>\n }\n @default {\n <div class=\"ng-sched-placeholder\">\n View '{{ store.viewMode() }}' not implemented yet.\n </div>\n }\n }\n </div>\n\n </main>\n</div>", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;width:100%;--mglon-schedule-primary-50: #e8f0fe;--mglon-schedule-primary-100: #d2e3fc;--mglon-schedule-primary-200: #a7cbfb;--mglon-schedule-primary-300: #7cacf8;--mglon-schedule-primary-400: #518ef5;--mglon-schedule-primary-500: #1a73e8;--mglon-schedule-primary-600: #1967d2;--mglon-schedule-primary-700: #185abc;--mglon-schedule-primary-800: #174ea6;--mglon-schedule-primary-900: #0d3a86;--mglon-schedule-primary-950: #08265a;--mglon-schedule-neutral-50: #fafafa;--mglon-schedule-neutral-100: #f5f5f5;--mglon-schedule-neutral-200: #e5e5e5;--mglon-schedule-neutral-300: #d4d4d4;--mglon-schedule-neutral-400: #a3a3a3;--mglon-schedule-neutral-500: #737373;--mglon-schedule-neutral-600: #525252;--mglon-schedule-neutral-700: #404040;--mglon-schedule-neutral-800: #262626;--mglon-schedule-neutral-900: #171717;--mglon-schedule-neutral-950: #0a0a0a;--mglon-schedule-primary: var(--mglon-schedule-primary-500);--mglon-schedule-on-primary: #ffffff;--mglon-schedule-surface: #ffffff;--mglon-schedule-on-surface: var(--mglon-schedule-neutral-900);--mglon-schedule-surface-variant: var(--mglon-schedule-neutral-100);--mglon-schedule-on-surface-variant: var(--mglon-schedule-neutral-700);--mglon-schedule-background: #ffffff;--mglon-schedule-on-background: var(--mglon-schedule-neutral-900);--mglon-schedule-border: var(--mglon-schedule-neutral-200);--mglon-schedule-text-primary: var(--mglon-schedule-neutral-900);--mglon-schedule-text-secondary: var(--mglon-schedule-neutral-500);--mglon-schedule-text-tertiary: var(--mglon-schedule-neutral-400);--mglon-schedule-hover: var(--mglon-schedule-neutral-100);--mglon-schedule-selection: var(--mglon-schedule-primary-50);--mglon-schedule-error: #ea4335;--mglon-schedule-on-error: #ffffff;--mglon-schedule-spacing-xs: 4px;--mglon-schedule-spacing-sm: 8px;--mglon-schedule-spacing-md: 16px;--mglon-schedule-spacing-lg: 24px;--mglon-schedule-padding-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-padding-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-padding-md: var(--mglon-schedule-spacing-md);--mglon-schedule-padding-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-margin-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-margin-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-margin-md: var(--mglon-schedule-spacing-md);--mglon-schedule-margin-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-gap-xs: var(--mglon-schedule-spacing-xs);--mglon-schedule-gap-sm: var(--mglon-schedule-spacing-sm);--mglon-schedule-gap-md: var(--mglon-schedule-spacing-md);--mglon-schedule-gap-lg: var(--mglon-schedule-spacing-lg);--mglon-schedule-border-width-sm: 1px;--mglon-schedule-border-width-md: 2px;--mglon-schedule-border-width-lg: 4px;--mglon-schedule-header-height: 48px;--mglon-schedule-sidebar-width: 60px;--mglon-schedule-cell-height: 48px;--mglon-schedule-radius-sm: 4px;--mglon-schedule-radius-md: 8px;--mglon-schedule-radius-lg: 16px;--mglon-schedule-radius-full: 9999px;--mglon-schedule-font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;--mglon-schedule-font-size-xs: 10px;--mglon-schedule-font-size-sm: 12px;--mglon-schedule-font-size-md: 14px;--mglon-schedule-font-size-lg: 16px;--mglon-schedule-shadow-sm: 0 1px 2px rgba(0, 0, 0, .1);--mglon-schedule-shadow-md: 0 2px 4px rgba(0, 0, 0, .15);--mglon-schedule-shadow-lg: 0 4px 8px rgba(0, 0, 0, .2);--mglon-schedule-z-sticky: 10;--mglon-schedule-z-header: 20;--mglon-schedule-hover-bg: rgba(0, 0, 0, .04);--mglon-schedule-focus-ring-color: var(--mglon-schedule-primary-200);--mglon-schedule-disabled-opacity: .5;--mglon-schedule-transition-duration: .2s;--mglon-schedule-transition-easing: cubic-bezier(.4, 0, .2, 1);--mglon-schedule-month-week-display: grid;--mglon-schedule-month-week-columns: repeat(7, 1fr);--mglon-schedule-month-week-min-height: 80px;--mglon-schedule-month-week-border-bottom-width: 1px}.ng-sched-schedule{display:grid;grid-template-rows:auto 1fr;height:100%;width:100%;background-color:var(--mglon-schedule-background);color:var(--mglon-schedule-on-background);font-family:var(--mglon-schedule-font-family)}.ng-sched-header{display:flex;align-items:center;justify-content:space-between;padding:var(--mglon-schedule-spacing-sm) var(--mglon-schedule-spacing-md);border-bottom:1px solid var(--mglon-schedule-border);background-color:var(--mglon-schedule-surface)}.ng-sched-header__nav{display:flex;align-items:center;gap:var(--mglon-schedule-spacing-sm)}.ng-sched-header__title{margin:0 0 0 var(--mglon-schedule-spacing-md);font-size:1.25rem;font-weight:500}.ng-sched-header__views{display:flex;gap:0;border:1px solid var(--mglon-schedule-border);border-radius:4px;overflow:hidden}.ng-sched-button{padding:6px 12px;border:1px solid var(--mglon-schedule-border);background-color:transparent;color:var(--mglon-schedule-on-surface);font-family:inherit;font-size:.875rem;cursor:pointer;transition:all .2s ease-in-out;border-radius:4px;outline:none;-webkit-user-select:none;user-select:none}.ng-sched-button--icon{padding:6px 10px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center}.ng-sched-button:hover:not(:disabled){background-color:var(--mglon-schedule-hover)}.ng-sched-button:active:not(:disabled){transform:scale(.98)}.ng-sched-button:focus-visible{outline:2px solid var(--mglon-schedule-primary);outline-offset:2px}.ng-sched-button--active{background-color:var(--mglon-schedule-selection);color:var(--mglon-schedule-primary);font-weight:500}.ng-sched-button:disabled{opacity:.5;cursor:not-allowed}.ng-sched-header__views .ng-sched-button{border:none;border-right:1px solid var(--mglon-schedule-border);border-radius:0}.ng-sched-header__views .ng-sched-button:last-child{border-right:none}.ng-sched-body{position:relative;height:100%;overflow:hidden;display:grid;grid-template-columns:auto 1fr;gap:0}.ng-sched-body:has(>:only-child){grid-template-columns:1fr}.ng-sched-body>:last-child{overflow:auto;min-width:0}.ng-sched-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:var(--mglon-schedule-spacing-lg);color:var(--mglon-schedule-text-secondary);font-size:1rem;text-align:center;gap:var(--mglon-schedule-spacing-md)}.ng-sched-placeholder:before{content:\"\\1f4c5\";font-size:3rem;opacity:.3}\n"] }]
3310
+ }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], headerUI: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerUI", required: false }] }], sidebarUI: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarUI", required: false }] }], gridUI: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridUI", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], useDynamicColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "useDynamicColors", required: false }] }], add: [{ type: i0.Output, args: ["add"] }], selectionStart: [{ type: i0.Output, args: ["selectionStart"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], selectionEnd: [{ type: i0.Output, args: ["selectionEnd"] }], viewChange: [{ type: i0.Output, args: ["viewChange"] }], dateRangeChange: [{ type: i0.Output, args: ["dateRangeChange"] }], resourceShow: [{ type: i0.Output, args: ["resourceShow"] }], resourceHide: [{ type: i0.Output, args: ["resourceHide"] }] } });
3311
+
3312
+ /**
3313
+ * Injection token for providing resource ID to child components
3314
+ */
3315
+ const RESOURCE_ID_TOKEN = new InjectionToken('RESOURCE_ID');
3316
+ /**
3317
+ * ResourceComponent - Declarative component representing a calendar resource
3318
+ *
3319
+ * Usage:
3320
+ * <mglon-resource
3321
+ * id="room-1"
3322
+ * name="Conference Room A"
3323
+ * [color]="'#0860c4'">
3324
+ * <mglon-event>...</mglon-event>
3325
+ * </mglon-resource>
3326
+ */
3327
+ class ResourceEvents {
3328
+ /** Unique identifier for the resource */
3329
+ id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
3330
+ /** Display name of the resource */
3331
+ name = input.required(...(ngDevMode ? [{ debugName: "name" }] : []));
3332
+ /** Color for the resource (border/background) */
3333
+ color = input(...(ngDevMode ? [undefined, { debugName: "color" }] : []));
3334
+ /** URL of image or initials for display */
3335
+ avatar = input(...(ngDevMode ? [undefined, { debugName: "avatar" }] : []));
3336
+ /** Additional description of the resource */
3337
+ description = input(...(ngDevMode ? [undefined, { debugName: "description" }] : []));
3338
+ /** Tags for filtering */
3339
+ tags = input(DEFAULT_RESOURCE_INPUTS.tags, ...(ngDevMode ? [{ debugName: "tags" }] : []));
3340
+ /** If true, events for this resource cannot be edited */
3341
+ isReadOnly = input(DEFAULT_RESOURCE_INPUTS.isReadOnly, ...(ngDevMode ? [{ debugName: "isReadOnly" }] : []));
3342
+ /** If true, this resource does not accept new events */
3343
+ isBlocked = input(DEFAULT_RESOURCE_INPUTS.isBlocked, ...(ngDevMode ? [{ debugName: "isBlocked" }] : []));
3344
+ /** If true, resource is active and visible. Default: true */
3345
+ isActive = input(DEFAULT_RESOURCE_INPUTS.isActive, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
3346
+ /** Flexible user-defined data */
3347
+ metadata = input(...(ngDevMode ? [undefined, { debugName: "metadata" }] : []));
3348
+ /** If true, allows resizing events for this resource. Default: true */
3349
+ resizableEvents = input(DEFAULT_RESOURCE_INPUTS.resizableEvents, ...(ngDevMode ? [{ debugName: "resizableEvents" }] : []));
3350
+ elRef = inject(ElementRef);
3351
+ store = inject(CalendarStore);
3352
+ ngAfterContentInit() {
3353
+ this._removeInvalidNodes();
3354
+ }
3355
+ _removeInvalidNodes() {
3356
+ const children = this.elRef.nativeElement.childNodes;
3357
+ children.forEach((node) => {
3358
+ if (node.nodeType === 1 && node.tagName !== 'MGLON-EVENT') {
3359
+ console.warn(`[ResourceEvents] The element <${node.tagName.toLowerCase()}> is not allowed inside mglon-resource-events. Only mglon-event components are allowed. This node will be removed.`);
3360
+ node.remove();
3361
+ }
3362
+ });
3363
+ }
3364
+ constructor() {
3365
+ // Sync isActive input changes to EventStore
3366
+ effect(() => {
3367
+ const shouldBeActive = this.isActive();
3368
+ const resourceId = this.id();
3369
+ if (resourceId) {
3370
+ if (shouldBeActive) {
3371
+ this.store.showResource(resourceId);
3372
+ }
3373
+ else {
3374
+ this.store.hideResource(resourceId);
3375
+ }
3376
+ }
3377
+ }, { allowSignalWrites: true });
3378
+ }
3379
+ ngOnInit() {
3380
+ this.registerResource();
3381
+ }
3382
+ /**
3383
+ * Lifecycle: Unregister resource on destroy
3384
+ */
3385
+ ngOnDestroy() {
3386
+ // Only unregister if the component was fully initialized
3387
+ try {
3388
+ const resourceId = this.id();
3389
+ if (resourceId) {
3390
+ this.store.unregisterResource(resourceId);
3391
+ }
3392
+ }
3393
+ catch (e) {
3394
+ // Ignore errors during cleanup (e.g., inputs not yet initialized in tests)
3395
+ }
3396
+ }
3397
+ registerResource() {
3398
+ const resource = {
3399
+ id: this.id(),
3400
+ name: this.name(),
3401
+ color: this.color(),
3402
+ avatar: this.avatar(),
3403
+ description: this.description(),
3404
+ tags: this.tags(),
3405
+ isReadOnly: this.isReadOnly(),
3406
+ isBlocked: this.isBlocked(),
3407
+ isActive: this.isActive(),
3408
+ resizableEvents: this.resizableEvents(),
3409
+ metadata: this.metadata()
3410
+ };
3411
+ this.store.registerResource(resource);
3412
+ }
3413
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResourceEvents, deps: [], target: i0.ɵɵFactoryTarget.Component });
3414
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: ResourceEvents, isStandalone: true, selector: "mglon-resource", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, avatar: { classPropertyName: "avatar", publicName: "avatar", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null }, isReadOnly: { classPropertyName: "isReadOnly", publicName: "isReadOnly", isSignal: true, isRequired: false, transformFunction: null }, isBlocked: { classPropertyName: "isBlocked", publicName: "isBlocked", isSignal: true, isRequired: false, transformFunction: null }, isActive: { classPropertyName: "isActive", publicName: "isActive", isSignal: true, isRequired: false, transformFunction: null }, metadata: { classPropertyName: "metadata", publicName: "metadata", isSignal: true, isRequired: false, transformFunction: null }, resizableEvents: { classPropertyName: "resizableEvents", publicName: "resizableEvents", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
3415
+ {
3416
+ provide: RESOURCE_ID_TOKEN,
3417
+ useFactory: (component) => component.id(),
3418
+ deps: [ResourceEvents]
3419
+ }
3420
+ ], ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [":host{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
3421
+ }
3422
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ResourceEvents, decorators: [{
3423
+ type: Component,
3424
+ args: [{ selector: 'mglon-resource', standalone: true, imports: [CommonModule], template: `<ng-content></ng-content>`, providers: [
3425
+ {
3426
+ provide: RESOURCE_ID_TOKEN,
3427
+ useFactory: (component) => component.id(),
3428
+ deps: [ResourceEvents]
3429
+ }
3430
+ ], styles: [":host{display:contents}\n"] }]
3431
+ }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], avatar: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatar", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], tags: [{ type: i0.Input, args: [{ isSignal: true, alias: "tags", required: false }] }], isReadOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "isReadOnly", required: false }] }], isBlocked: [{ type: i0.Input, args: [{ isSignal: true, alias: "isBlocked", required: false }] }], isActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "isActive", required: false }] }], metadata: [{ type: i0.Input, args: [{ isSignal: true, alias: "metadata", required: false }] }], resizableEvents: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizableEvents", required: false }] }] } });
3432
+
3433
+ class Event {
3434
+ id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
3435
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
3436
+ startDate = input.required(...(ngDevMode ? [{ debugName: "startDate" }] : []));
3437
+ endDate = input.required(...(ngDevMode ? [{ debugName: "endDate" }] : []));
3438
+ color = input(...(ngDevMode ? [undefined, { debugName: "color" }] : []));
3439
+ allDay = input(DEFAULT_EVENT_INPUTS.allDay, ...(ngDevMode ? [{ debugName: "allDay" }] : []));
3440
+ description = input(DEFAULT_EVENT_INPUTS.description, ...(ngDevMode ? [{ debugName: "description" }] : []));
3441
+ data = input(DEFAULT_EVENT_INPUTS.data, ...(ngDevMode ? [{ debugName: "data" }] : []));
3442
+ /** Optional explicit resource ID (if not nested in mglon-resource) */
3443
+ resourceId = input(...(ngDevMode ? [undefined, { debugName: "resourceId" }] : []));
3444
+ parentResourceId = inject(RESOURCE_ID_TOKEN, { optional: true });
3445
+ /** If true, the event cannot be edited */
3446
+ isReadOnly = input(false, ...(ngDevMode ? [{ debugName: "isReadOnly" }] : []));
3447
+ /** If true, the event is visually blocked (e.g., maintenance) */
3448
+ isBlocked = input(false, ...(ngDevMode ? [{ debugName: "isBlocked" }] : []));
3449
+ /** Flexible user-defined data */
3450
+ metadata = input(...(ngDevMode ? [undefined, { debugName: "metadata" }] : []));
3451
+ /** Tags for filtering and styling */
3452
+ tags = input([], ...(ngDevMode ? [{ debugName: "tags" }] : []));
3453
+ // --- Interaction Outputs ---
3454
+ eventClick = output();
3455
+ eventDblClick = output();
3456
+ eventContextMenu = output();
3457
+ eventMouseEnter = output();
3458
+ eventMouseLeave = output();
3459
+ eventResizeStart = output();
3460
+ eventResize = output();
3461
+ eventResizeEnd = output();
3462
+ eventDragStart = output();
3463
+ eventDrag = output();
3464
+ eventDragEnd = output();
3465
+ destroy$ = new Subject();
3466
+ store = inject(CalendarStore);
3467
+ ngOnInit() {
3468
+ this.registerEvent();
3469
+ this.setupInteractionListeners();
3470
+ }
3471
+ setupInteractionListeners() {
3472
+ this.store.getInteractions()
3473
+ .pipe(filter(e => e.eventId === this.id()), takeUntil(this.destroy$))
3474
+ .subscribe(e => {
3475
+ switch (e.type) {
3476
+ case 'click':
3477
+ this.eventClick.emit(e.payload);
3478
+ break;
3479
+ case 'dblclick':
3480
+ this.eventDblClick.emit(e.payload);
3481
+ break;
3482
+ case 'contextmenu':
3483
+ this.eventContextMenu.emit(e.payload);
3484
+ break;
3485
+ case 'mouseenter':
3486
+ this.eventMouseEnter.emit(e.payload);
3487
+ break;
3488
+ case 'mouseleave':
3489
+ this.eventMouseLeave.emit(e.payload);
3490
+ break;
3491
+ case 'resizeStart':
3492
+ this.eventResizeStart.emit(e.payload);
3493
+ break;
3494
+ case 'resize':
3495
+ this.eventResize.emit(e.payload);
3496
+ break;
3497
+ case 'resizeEnd':
3498
+ this.eventResizeEnd.emit(e.payload);
3499
+ break;
3500
+ case 'dragStart':
3501
+ this.eventDragStart.emit(e.payload);
3502
+ break;
3503
+ case 'drag':
3504
+ this.eventDrag.emit(e.payload);
3505
+ break;
3506
+ case 'dragEnd':
3507
+ this.eventDragEnd.emit(e.payload);
3508
+ break;
3509
+ }
3510
+ });
3511
+ }
3512
+ /**
3513
+ * Lifecycle: Unregister event from store
3514
+ */
3515
+ ngOnDestroy() {
3516
+ this.destroy$.next();
3517
+ this.destroy$.complete();
3518
+ // Only unregister if the component was fully initialized
3519
+ try {
3520
+ const eventId = this.id();
3521
+ if (eventId) {
3522
+ this.store.unregisterEvent(eventId);
3523
+ }
3524
+ }
3525
+ catch (e) {
3526
+ // Ignore errors during cleanup (e.g., inputs not yet initialized in tests)
3527
+ }
3528
+ }
3529
+ registerEvent() {
3530
+ const event = {
3531
+ id: this.id(),
3532
+ resourceId: this.resourceId() || this.parentResourceId || undefined,
3533
+ title: this.title(),
3534
+ description: this.description(),
3535
+ tags: this.tags(),
3536
+ start: this.startDate(),
3537
+ end: this.endDate(),
3538
+ color: this.color(),
3539
+ isReadOnly: this.isReadOnly(),
3540
+ isBlocked: this.isBlocked(),
3541
+ isAllDay: this.allDay(),
3542
+ metadata: this.metadata(),
3543
+ type: 'event'
3544
+ };
3545
+ this.store.registerEvent(event);
3546
+ }
3547
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Event, deps: [], target: i0.ɵɵFactoryTarget.Component });
3548
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: Event, isStandalone: true, selector: "mglon-event", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, startDate: { classPropertyName: "startDate", publicName: "startDate", isSignal: true, isRequired: true, transformFunction: null }, endDate: { classPropertyName: "endDate", publicName: "endDate", isSignal: true, isRequired: true, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, allDay: { classPropertyName: "allDay", publicName: "allDay", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, resourceId: { classPropertyName: "resourceId", publicName: "resourceId", isSignal: true, isRequired: false, transformFunction: null }, isReadOnly: { classPropertyName: "isReadOnly", publicName: "isReadOnly", isSignal: true, isRequired: false, transformFunction: null }, isBlocked: { classPropertyName: "isBlocked", publicName: "isBlocked", isSignal: true, isRequired: false, transformFunction: null }, metadata: { classPropertyName: "metadata", publicName: "metadata", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { eventClick: "eventClick", eventDblClick: "eventDblClick", eventContextMenu: "eventContextMenu", eventMouseEnter: "eventMouseEnter", eventMouseLeave: "eventMouseLeave", eventResizeStart: "eventResizeStart", eventResize: "eventResize", eventResizeEnd: "eventResizeEnd", eventDragStart: "eventDragStart", eventDrag: "eventDrag", eventDragEnd: "eventDragEnd" }, ngImport: i0, template: `<!-- Events are managed declaratively -->`, isInline: true, styles: [":host{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
3549
+ }
3550
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Event, decorators: [{
3551
+ type: Component,
3552
+ args: [{ selector: 'mglon-event', standalone: true, imports: [CommonModule], template: `<!-- Events are managed declaratively -->`, styles: [":host{display:none}\n"] }]
3553
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], startDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "startDate", required: true }] }], endDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "endDate", required: true }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], allDay: [{ type: i0.Input, args: [{ isSignal: true, alias: "allDay", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], resourceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "resourceId", required: false }] }], isReadOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "isReadOnly", required: false }] }], isBlocked: [{ type: i0.Input, args: [{ isSignal: true, alias: "isBlocked", required: false }] }], metadata: [{ type: i0.Input, args: [{ isSignal: true, alias: "metadata", required: false }] }], tags: [{ type: i0.Input, args: [{ isSignal: true, alias: "tags", required: false }] }], eventClick: [{ type: i0.Output, args: ["eventClick"] }], eventDblClick: [{ type: i0.Output, args: ["eventDblClick"] }], eventContextMenu: [{ type: i0.Output, args: ["eventContextMenu"] }], eventMouseEnter: [{ type: i0.Output, args: ["eventMouseEnter"] }], eventMouseLeave: [{ type: i0.Output, args: ["eventMouseLeave"] }], eventResizeStart: [{ type: i0.Output, args: ["eventResizeStart"] }], eventResize: [{ type: i0.Output, args: ["eventResize"] }], eventResizeEnd: [{ type: i0.Output, args: ["eventResizeEnd"] }], eventDragStart: [{ type: i0.Output, args: ["eventDragStart"] }], eventDrag: [{ type: i0.Output, args: ["eventDrag"] }], eventDragEnd: [{ type: i0.Output, args: ["eventDragEnd"] }] } });
3554
+
3555
+ const DAY_MAP = {
3556
+ 'Sun': RRule.SU,
3557
+ 'Mon': RRule.MO,
3558
+ 'Tue': RRule.TU,
3559
+ 'Wed': RRule.WE,
3560
+ 'Thu': RRule.TH,
3561
+ 'Fri': RRule.FR,
3562
+ 'Sat': RRule.SA
3563
+ };
3564
+ /**
3565
+ * Expands a recurrent event into a list of individual event instances for a given date range.
3566
+ * Each instance has a composite ID: {parentID}_{timestamp}
3567
+ *
3568
+ * @param recurrentEvent The recurrence definition
3569
+ * @param range The search range (DateRange)
3570
+ * @returns Array of Event instances
3571
+ */
3572
+ function expandRecurrentEvent(recurrentEvent, range) {
3573
+ const { start: rangeStart, end: rangeEnd } = range;
3574
+ const rule = recurrentEvent.recurrenceRule;
3575
+ // Mapping frequency
3576
+ const freqMap = {
3577
+ 'daily': RRule.DAILY,
3578
+ 'weekly': RRule.WEEKLY,
3579
+ 'monthly': RRule.MONTHLY,
3580
+ 'yearly': RRule.YEARLY
3581
+ };
3582
+ // Duration of the original event
3583
+ const duration = differenceInMilliseconds(recurrentEvent.end, recurrentEvent.start);
3584
+ // Configure options for RRule
3585
+ const options = {
3586
+ freq: freqMap[rule.type],
3587
+ dtstart: recurrentEvent.start,
3588
+ interval: rule.interval || 1,
3589
+ };
3590
+ if (rule.until)
3591
+ options.until = rule.until;
3592
+ if (rule.count)
3593
+ options.count = rule.count;
3594
+ if (rule.byDay)
3595
+ options.byweekday = rule.byDay.map(day => DAY_MAP[day]);
3596
+ if (rule.byMonth)
3597
+ options.bymonth = rule.byMonth;
3598
+ if (rule.byMonthDay)
3599
+ options.bymonthday = rule.byMonthDay;
3600
+ if (rule.bySetPos)
3601
+ options.bysetpos = rule.bySetPos;
3602
+ if (rule.weekStart)
3603
+ options.wkst = DAY_MAP[rule.weekStart];
3604
+ const rrule = new RRule(options);
3605
+ // Get occurrences within the requested range
3606
+ const occurrences = rrule.between(rangeStart, rangeEnd, true);
3607
+ // Filter out exceptions if they exist (comparing timestamps for precision)
3608
+ const exceptions = recurrentEvent.recurrenceExceptions?.map(d => d.getTime()) || [];
3609
+ return occurrences
3610
+ .filter(date => !exceptions.includes(date.getTime()))
3611
+ .map(date => {
3612
+ // Calculate individual instance end date based on original duration
3613
+ const instanceEnd = addMilliseconds(date, duration);
3614
+ // We extract everything from the recurrent model EXCEPT the recurrence-specific
3615
+ // fields and the 'type' field, to strictly conform to the Event interface.
3616
+ const { recurrenceRule, recurrenceExceptions, type: _parentType, // ignore the 'recurrent' type
3617
+ ...baseProps } = recurrentEvent;
3618
+ return {
3619
+ ...baseProps,
3620
+ id: `${recurrentEvent.id}_${date.getTime()}`,
3621
+ start: date,
3622
+ end: instanceEnd,
3623
+ type: 'event', // Behave as a normal event for rendering
3624
+ isRecurrenceInstance: true,
3625
+ parentRecurrenceId: recurrentEvent.id,
3626
+ recurrenceDate: date
3627
+ };
3628
+ });
3629
+ }
3630
+
3631
+ class RecurrentEvent {
3632
+ id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
3633
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
3634
+ startDate = input.required(...(ngDevMode ? [{ debugName: "startDate" }] : []));
3635
+ endDate = input.required(...(ngDevMode ? [{ debugName: "endDate" }] : []));
3636
+ color = input(...(ngDevMode ? [undefined, { debugName: "color" }] : []));
3637
+ allDay = input(DEFAULT_EVENT_INPUTS.allDay, ...(ngDevMode ? [{ debugName: "allDay" }] : []));
3638
+ description = input(DEFAULT_EVENT_INPUTS.description, ...(ngDevMode ? [{ debugName: "description" }] : []));
3639
+ data = input(DEFAULT_EVENT_INPUTS.data, ...(ngDevMode ? [{ debugName: "data" }] : []));
3640
+ /** Optional explicit resource ID (if not nested in mglon-resource) */
3641
+ resourceId = input(...(ngDevMode ? [undefined, { debugName: "resourceId" }] : []));
3642
+ parentResourceId = inject(RESOURCE_ID_TOKEN, { optional: true });
3643
+ /** If true, the event cannot be edited */
3644
+ isReadOnly = input(false, ...(ngDevMode ? [{ debugName: "isReadOnly" }] : []));
3645
+ /** If true, the event is visually blocked (e.g., maintenance) */
3646
+ isBlocked = input(false, ...(ngDevMode ? [{ debugName: "isBlocked" }] : []));
3647
+ /** Flexible user-defined data */
3648
+ metadata = input(...(ngDevMode ? [undefined, { debugName: "metadata" }] : []));
3649
+ /** Tags for filtering and styling */
3650
+ tags = input([], ...(ngDevMode ? [{ debugName: "tags" }] : []));
3651
+ /** Recurrence rule defining the pattern */
3652
+ recurrenceRule = input.required(...(ngDevMode ? [{ debugName: "recurrenceRule" }] : [])); // TODO: Type this properly
3653
+ /** Specific dates to exclude */
3654
+ recurrenceExceptions = input(...(ngDevMode ? [undefined, { debugName: "recurrenceExceptions" }] : []));
3655
+ // --- Interaction Outputs ---
3656
+ eventClick = output();
3657
+ eventDblClick = output();
3658
+ eventContextMenu = output();
3659
+ eventMouseEnter = output();
3660
+ eventMouseLeave = output();
3661
+ destroy$ = new Subject();
3662
+ store = inject(CalendarStore);
3663
+ constructor() {
3664
+ effect(() => {
3665
+ // Trigger this effect when range OR any relevant input signal changes
3666
+ this.store.viewRange();
3667
+ this.id();
3668
+ this.title();
3669
+ this.startDate();
3670
+ this.endDate();
3671
+ this.recurrenceRule();
3672
+ untracked(() => {
3673
+ this.registerEvent();
3674
+ });
3675
+ });
3676
+ }
3677
+ ngOnInit() {
3678
+ this.setupInteractionListeners();
3679
+ }
3680
+ setupInteractionListeners() {
3681
+ this.store.getInteractions()
3682
+ .pipe(filter(e => e.eventId === this.id()), takeUntil(this.destroy$))
3683
+ .subscribe(e => {
3684
+ switch (e.type) {
3685
+ case 'click':
3686
+ this.eventClick.emit(e.payload);
3687
+ break;
3688
+ case 'dblclick':
3689
+ this.eventDblClick.emit(e.payload);
3690
+ break;
3691
+ case 'contextmenu':
3692
+ this.eventContextMenu.emit(e.payload);
3693
+ break;
3694
+ case 'mouseenter':
3695
+ this.eventMouseEnter.emit(e.payload);
3696
+ break;
3697
+ case 'mouseleave':
3698
+ this.eventMouseLeave.emit(e.payload);
3699
+ break;
3700
+ }
3701
+ });
3702
+ }
3703
+ currentInstanceIds = [];
3704
+ /**
3705
+ * Lifecycle: Unregister event and all its instances from store
3706
+ */
3707
+ ngOnDestroy() {
3708
+ this.destroy$.next();
3709
+ this.destroy$.complete();
3710
+ this.cleanup();
3711
+ }
3712
+ cleanup() {
3713
+ try {
3714
+ // 1. Unregister all instances
3715
+ this.currentInstanceIds.forEach(id => this.store.unregisterEvent(id));
3716
+ this.currentInstanceIds = [];
3717
+ // 2. Unregister parent
3718
+ const parentId = this.id();
3719
+ if (parentId) {
3720
+ this.store.unregisterEvent(parentId);
3721
+ }
3722
+ }
3723
+ catch (e) {
3724
+ // Ignore errors during cleanup
3725
+ }
3726
+ }
3727
+ registerEvent() {
3728
+ // 1. Prepare Parent Master
3729
+ const parentEvent = {
3730
+ id: this.id(),
3731
+ resourceId: this.resourceId() || this.parentResourceId || undefined,
3732
+ title: this.title(),
3733
+ description: this.description(),
3734
+ tags: this.tags(),
3735
+ start: this.startDate(),
3736
+ end: this.endDate(),
3737
+ color: this.color(),
3738
+ isReadOnly: this.isReadOnly(),
3739
+ isBlocked: this.isBlocked(),
3740
+ isAllDay: this.allDay(),
3741
+ metadata: this.metadata(),
3742
+ type: 'recurrent',
3743
+ recurrenceRule: this.recurrenceRule(),
3744
+ recurrenceExceptions: this.recurrenceExceptions()
3745
+ };
3746
+ // 2. Cleanup previous instances before re-registering
3747
+ this.currentInstanceIds.forEach(id => this.store.unregisterEvent(id));
3748
+ this.currentInstanceIds = [];
3749
+ // 3. Register the parent recurrent event (Master definition)
3750
+ this.store.registerEvent(parentEvent);
3751
+ // 4. Expand and register instances for the current range
3752
+ const range = this.store.viewRange();
3753
+ const instances = expandRecurrentEvent(parentEvent, range);
3754
+ instances.forEach(instance => {
3755
+ this.store.registerEvent(instance);
3756
+ this.currentInstanceIds.push(instance.id);
3757
+ });
3758
+ }
3759
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RecurrentEvent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3760
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: RecurrentEvent, isStandalone: true, selector: "mglon-recurrent-event", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, startDate: { classPropertyName: "startDate", publicName: "startDate", isSignal: true, isRequired: true, transformFunction: null }, endDate: { classPropertyName: "endDate", publicName: "endDate", isSignal: true, isRequired: true, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, allDay: { classPropertyName: "allDay", publicName: "allDay", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, resourceId: { classPropertyName: "resourceId", publicName: "resourceId", isSignal: true, isRequired: false, transformFunction: null }, isReadOnly: { classPropertyName: "isReadOnly", publicName: "isReadOnly", isSignal: true, isRequired: false, transformFunction: null }, isBlocked: { classPropertyName: "isBlocked", publicName: "isBlocked", isSignal: true, isRequired: false, transformFunction: null }, metadata: { classPropertyName: "metadata", publicName: "metadata", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null }, recurrenceRule: { classPropertyName: "recurrenceRule", publicName: "recurrenceRule", isSignal: true, isRequired: true, transformFunction: null }, recurrenceExceptions: { classPropertyName: "recurrenceExceptions", publicName: "recurrenceExceptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { eventClick: "eventClick", eventDblClick: "eventDblClick", eventContextMenu: "eventContextMenu", eventMouseEnter: "eventMouseEnter", eventMouseLeave: "eventMouseLeave" }, ngImport: i0, template: `<!-- Events are managed declaratively -->`, isInline: true, styles: [":host{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
3761
+ }
3762
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RecurrentEvent, decorators: [{
3763
+ type: Component,
3764
+ args: [{ selector: 'mglon-recurrent-event', standalone: true, imports: [CommonModule], template: `<!-- Events are managed declaratively -->`, styles: [":host{display:none}\n"] }]
3765
+ }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], startDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "startDate", required: true }] }], endDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "endDate", required: true }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], allDay: [{ type: i0.Input, args: [{ isSignal: true, alias: "allDay", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], resourceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "resourceId", required: false }] }], isReadOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "isReadOnly", required: false }] }], isBlocked: [{ type: i0.Input, args: [{ isSignal: true, alias: "isBlocked", required: false }] }], metadata: [{ type: i0.Input, args: [{ isSignal: true, alias: "metadata", required: false }] }], tags: [{ type: i0.Input, args: [{ isSignal: true, alias: "tags", required: false }] }], recurrenceRule: [{ type: i0.Input, args: [{ isSignal: true, alias: "recurrenceRule", required: true }] }], recurrenceExceptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "recurrenceExceptions", required: false }] }], eventClick: [{ type: i0.Output, args: ["eventClick"] }], eventDblClick: [{ type: i0.Output, args: ["eventDblClick"] }], eventContextMenu: [{ type: i0.Output, args: ["eventContextMenu"] }], eventMouseEnter: [{ type: i0.Output, args: ["eventMouseEnter"] }], eventMouseLeave: [{ type: i0.Output, args: ["eventMouseLeave"] }] } });
3766
+
3767
+ class FabButtonComponent {
3768
+ size = input('lg', ...(ngDevMode ? [{ debugName: "size" }] : []));
3769
+ color = input('surface', ...(ngDevMode ? [{ debugName: "color" }] : []));
3770
+ // Optional: extended FAB (with text) vs standard (icon only) could be handled via CSS classes check or another input
3771
+ extended = input(false, ...(ngDevMode ? [{ debugName: "extended" }] : []));
3772
+ classes = computed(() => {
3773
+ return [
3774
+ 'mglon-fab-button',
3775
+ `size-${this.size()}`,
3776
+ `color-${this.color()}`,
3777
+ this.extended() ? 'extended' : ''
3778
+ ].join(' ');
3779
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : []));
3780
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FabButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3781
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: FabButtonComponent, isStandalone: true, selector: "button[mglon-fab-button], a[mglon-fab-button]", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, extended: { classPropertyName: "extended", publicName: "extended", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.data-size": "size()" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;position:relative;box-sizing:border-box;border:none;outline:none;cursor:pointer;-webkit-user-select:none;user-select:none;vertical-align:middle;background-clip:padding-box;text-decoration:none;font-family:var(--mglon-schedule-font-family);border-radius:var(--mglon-fab-radius, 16px);transition:var(--mglon-fab-transition, box-shadow .28s var(--mglon-schedule-transition-easing), background-color .28s var(--mglon-schedule-transition-easing));box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}:host:hover{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d}:host.size-md{height:var(--mglon-fab-md-size, 48px);min-width:var(--mglon-fab-md-size, 48px)}:host.size-md.extended{padding:0 20px;border-radius:var(--mglon-fab-extended-radius, 24px)}:host.size-md:not(.extended){width:var(--mglon-fab-md-size, 48px);border-radius:50%}:host.size-lg{height:var(--mglon-fab-lg-size, 56px);min-width:var(--mglon-fab-lg-size, 56px)}:host.size-lg.extended{padding:0 24px;border-radius:var(--mglon-fab-lg-extended-radius, 28px);font-size:14px;font-weight:500;letter-spacing:.25px}:host.size-lg:not(.extended){width:var(--mglon-fab-lg-size, 56px);border-radius:50%;font-size:24px}:host.color-surface{background-color:var(--mglon-fab-surface-bg, var(--mglon-schedule-surface));color:var(--mglon-fab-surface-color, var(--mglon-schedule-text-primary))}:host.color-primary{background-color:var(--mglon-fab-primary-bg, var(--mglon-schedule-primary));color:var(--mglon-fab-primary-color, var(--mglon-schedule-on-primary))}:host.color-primary:hover{background-color:var(--mglon-fab-primary-hover-bg, #185abc)}:host.color-secondary{background-color:var(--mglon-fab-secondary-bg, var(--mglon-schedule-selection));color:var(--mglon-fab-secondary-color, var(--mglon-schedule-primary))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
3782
+ }
3783
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FabButtonComponent, decorators: [{
3784
+ type: Component,
3785
+ args: [{ selector: 'button[mglon-fab-button], a[mglon-fab-button]', standalone: true, imports: [CommonModule], template: '<ng-content></ng-content>', host: {
3786
+ '[class]': 'classes()',
3787
+ '[attr.data-size]': 'size()'
3788
+ }, styles: [":host{display:inline-flex;align-items:center;justify-content:center;position:relative;box-sizing:border-box;border:none;outline:none;cursor:pointer;-webkit-user-select:none;user-select:none;vertical-align:middle;background-clip:padding-box;text-decoration:none;font-family:var(--mglon-schedule-font-family);border-radius:var(--mglon-fab-radius, 16px);transition:var(--mglon-fab-transition, box-shadow .28s var(--mglon-schedule-transition-easing), background-color .28s var(--mglon-schedule-transition-easing));box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}:host:hover{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d}:host.size-md{height:var(--mglon-fab-md-size, 48px);min-width:var(--mglon-fab-md-size, 48px)}:host.size-md.extended{padding:0 20px;border-radius:var(--mglon-fab-extended-radius, 24px)}:host.size-md:not(.extended){width:var(--mglon-fab-md-size, 48px);border-radius:50%}:host.size-lg{height:var(--mglon-fab-lg-size, 56px);min-width:var(--mglon-fab-lg-size, 56px)}:host.size-lg.extended{padding:0 24px;border-radius:var(--mglon-fab-lg-extended-radius, 28px);font-size:14px;font-weight:500;letter-spacing:.25px}:host.size-lg:not(.extended){width:var(--mglon-fab-lg-size, 56px);border-radius:50%;font-size:24px}:host.color-surface{background-color:var(--mglon-fab-surface-bg, var(--mglon-schedule-surface));color:var(--mglon-fab-surface-color, var(--mglon-schedule-text-primary))}:host.color-primary{background-color:var(--mglon-fab-primary-bg, var(--mglon-schedule-primary));color:var(--mglon-fab-primary-color, var(--mglon-schedule-on-primary))}:host.color-primary:hover{background-color:var(--mglon-fab-primary-hover-bg, #185abc)}:host.color-secondary{background-color:var(--mglon-fab-secondary-bg, var(--mglon-schedule-selection));color:var(--mglon-fab-secondary-color, var(--mglon-schedule-primary))}\n"] }]
3789
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], extended: [{ type: i0.Input, args: [{ isSignal: true, alias: "extended", required: false }] }] } });
3790
+
3791
+ /*
3792
+ * Public API Surface of ng-scheduler
3793
+ */
3794
+
3795
+ /**
3796
+ * Generated bundle index. Do not edit.
3797
+ */
3798
+
3799
+ export { ButtonComponent, ButtonGroupComponent, DEFAULT_UI_CONFIG, Event, FabButtonComponent, IconButtonComponent, RESOURCE_ID_TOKEN, RecurrentEvent, ResourceEvents, ResourceView, Schedule, WeekView };
3800
+ //# sourceMappingURL=mglon-schedule.mjs.map