mn-angular-lib 1.0.118 → 1.0.119
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.
|
@@ -9,7 +9,7 @@ import * as i1$1 from '@angular/forms';
|
|
|
9
9
|
import { NgControl, Validators, FormsModule, NG_VALUE_ACCESSOR, FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
|
10
10
|
import { HttpClient, HttpErrorResponse, HttpStatusCode, HttpParams } from '@angular/common/http';
|
|
11
11
|
import JSON5 from 'json5';
|
|
12
|
-
import { LucideFile, LucideImagePlus, LucideTrash2, LucideUpload, LucideX, LucideChevronLeft, LucideChevronRight, LucideArrowLeft, LucideArrowRight, LucideCheck, LucideFilter, LucideFunnel, LucideDynamicIcon } from '@lucide/angular';
|
|
12
|
+
import { LucideFile, LucideImagePlus, LucideTrash2, LucideUpload, LucideX, LucideCalendarDays, LucideChevronLeft, LucideChevronRight, LucideArrowLeft, LucideArrowRight, LucideCheck, LucideFilter, LucideFunnel, LucideDynamicIcon } from '@lucide/angular';
|
|
13
13
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
14
14
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
15
15
|
|
|
@@ -2889,7 +2889,35 @@ class MnDatetime {
|
|
|
2889
2889
|
get resolvedMode() {
|
|
2890
2890
|
return this.props.mode ?? 'datetime-local';
|
|
2891
2891
|
}
|
|
2892
|
+
/** Whether the control renders as an icon-only button rather than a full input. */
|
|
2893
|
+
get iconOnly() {
|
|
2894
|
+
return this.props.iconOnly === true;
|
|
2895
|
+
}
|
|
2896
|
+
/**
|
|
2897
|
+
* Accessible name for the input. Prefers an explicit ariaLabel/label; for the
|
|
2898
|
+
* icon-only variant — which has no visible text — it falls back to the
|
|
2899
|
+
* placeholder so the button is never left unnamed.
|
|
2900
|
+
*/
|
|
2901
|
+
get resolvedAriaLabel() {
|
|
2902
|
+
const explicit = this.uiConfig.ariaLabel || this.uiConfig.label || this.props.label;
|
|
2903
|
+
if (explicit)
|
|
2904
|
+
return explicit;
|
|
2905
|
+
return this.iconOnly ? (this.uiConfig.placeholder || this.props.placeholder || null) : null;
|
|
2906
|
+
}
|
|
2907
|
+
/** Lucide icon size (px) tracking the field size, used only in the icon-only variant. */
|
|
2908
|
+
get iconSize() {
|
|
2909
|
+
if (this.props.size === 'sm')
|
|
2910
|
+
return 16;
|
|
2911
|
+
if (this.props.size === 'lg')
|
|
2912
|
+
return 20;
|
|
2913
|
+
return 18;
|
|
2914
|
+
}
|
|
2892
2915
|
get inputClasses() {
|
|
2916
|
+
// Icon-only: the input becomes a transparent click target filling the icon box,
|
|
2917
|
+
// which carries the visible styling. `iconBoxClasses` provides the box itself.
|
|
2918
|
+
if (this.iconOnly) {
|
|
2919
|
+
return 'absolute inset-0 h-full w-full cursor-pointer opacity-0';
|
|
2920
|
+
}
|
|
2893
2921
|
return mnDatetimeVariants({
|
|
2894
2922
|
size: this.props.size,
|
|
2895
2923
|
borderRadius: this.props.borderRadius,
|
|
@@ -2898,12 +2926,30 @@ class MnDatetime {
|
|
|
2898
2926
|
hover: this.props.hover,
|
|
2899
2927
|
});
|
|
2900
2928
|
}
|
|
2929
|
+
/**
|
|
2930
|
+
* Classes for the icon-only box: the same border/background/radius/hover the full
|
|
2931
|
+
* input would wear (reused from the variant), made a positioning context for the
|
|
2932
|
+
* overlaid input, with a `focus-within` ring standing in for the transparent
|
|
2933
|
+
* input's own focus outline.
|
|
2934
|
+
*/
|
|
2935
|
+
get iconBoxClasses() {
|
|
2936
|
+
return [
|
|
2937
|
+
mnDatetimeVariants({
|
|
2938
|
+
size: this.props.size,
|
|
2939
|
+
borderRadius: this.props.borderRadius,
|
|
2940
|
+
shadow: this.props.shadow,
|
|
2941
|
+
hover: this.props.hover,
|
|
2942
|
+
}),
|
|
2943
|
+
'relative inline-flex items-center justify-center text-base-content/70',
|
|
2944
|
+
'focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-primary',
|
|
2945
|
+
].join(' ');
|
|
2946
|
+
}
|
|
2901
2947
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnDatetime, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2902
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnDatetime, isStandalone: true, selector: "mn-lib-datetime", inputs: { props: "props" }, host: { properties: { "style.display": "props?.fullWidth ? 'block' : null", "style.width": "props?.fullWidth ? '100%' : null" } }, ngImport: i0, template: "<div class=\"flex flex-col h-full\" [class.is-fullwidth]=\"props.fullWidth\">\n @if (uiConfig.label || props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ uiConfig.label || props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <input\n #dateInput\n [id]=\"resolvedId\"\n [attr.name]=\"resolvedName\"\n [type]=\"resolvedMode\"\n [attr.placeholder]=\"uiConfig.placeholder || props.placeholder || null\"\n [attr.aria-label]=\"
|
|
2948
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnDatetime, isStandalone: true, selector: "mn-lib-datetime", inputs: { props: "props" }, host: { properties: { "style.display": "props?.fullWidth ? 'block' : null", "style.width": "props?.fullWidth ? '100%' : null" } }, ngImport: i0, template: "<div class=\"flex flex-col h-full\" [class.is-fullwidth]=\"props.fullWidth\">\n @if (uiConfig.label || props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ uiConfig.label || props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n @if (iconOnly) {\n <!--\n Icon-only variant: a bordered box holding a calendar icon, with the native\n input layered on top at zero opacity. The box carries the look (border,\n background, radius, hover, focus ring); the input stays a real, focusable\n form control that opens the picker on click. `focus-within` on the box\n surfaces the keyboard focus ring, since the input itself is transparent.\n -->\n <div [ngClass]=\"iconBoxClasses\">\n <svg [size]=\"iconSize\" lucideCalendarDays aria-hidden=\"true\" class=\"pointer-events-none\"></svg>\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n }\n\n @if (showError) {\n @if (props.showAllErrors) {\n <div class=\"flex flex-col gap-y-1\">\n @for (error of errorMessages; track $index) {\n <mn-error-message [errorMessage]=\"error\" [id]=\"resolvedId + '-' + $index\"></mn-error-message>\n }\n </div>\n } @else {\n @if (errorMessage !== null) {\n <mn-error-message [errorMessage]=\"errorMessage\" [id]=\"resolvedId\"></mn-error-message>\n }\n }\n }\n</div>\n\n<!-- Single input definition, rendered either bare or inside the icon box above,\n so both variants share exactly the same bindings and behaviour. -->\n<ng-template #inputTemplate>\n <input\n #dateInput\n [id]=\"resolvedId\"\n [attr.name]=\"resolvedName\"\n [type]=\"resolvedMode\"\n [attr.placeholder]=\"uiConfig.placeholder || props.placeholder || null\"\n [attr.aria-label]=\"resolvedAriaLabel\"\n [attr.aria-invalid]=\"showError || null\"\n [attr.aria-describedby]=\"showError ? resolvedId + '-error' : null\"\n [disabled]=\"isDisabled\"\n [attr.min]=\"props.min || null\"\n [attr.max]=\"props.max || null\"\n [attr.step]=\"props.step || null\"\n [value]=\"value ?? ''\"\n [ngClass]=\"inputClasses\"\n (click)=\"handleClick(dateInput)\"\n (input)=\"handleInput(($any($event.target)).value)\"\n (blur)=\"handleBlur()\"\n />\n</ng-template>\n", styles: ["input::-webkit-calendar-picker-indicator{cursor:pointer}@media(pointer:coarse){input[type=date],input[type=datetime-local],input[type=time],input[type=month],input[type=week]{-webkit-appearance:none;appearance:none;min-width:0;box-sizing:border-box}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MnErrorMessage, selector: "mn-error-message", inputs: ["errorMessage", "id"] }, { kind: "component", type: LucideCalendarDays, selector: "svg[lucideCalendarDays]" }] });
|
|
2903
2949
|
}
|
|
2904
2950
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnDatetime, decorators: [{
|
|
2905
2951
|
type: Component,
|
|
2906
|
-
args: [{ selector: 'mn-lib-datetime', standalone: true, imports: [NgClass, MnErrorMessage], host: {
|
|
2952
|
+
args: [{ selector: 'mn-lib-datetime', standalone: true, imports: [NgClass, NgTemplateOutlet, MnErrorMessage, LucideCalendarDays], host: {
|
|
2907
2953
|
// Native date/time inputs have a platform-specific intrinsic width. Without an
|
|
2908
2954
|
// explicit host width the inline host collapses to that intrinsic size, so the
|
|
2909
2955
|
// input's `w-full` (width:100%) resolves against a content-sized box and fails to
|
|
@@ -2912,7 +2958,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2912
2958
|
// when fullWidth is requested so 100% has something to fill.
|
|
2913
2959
|
'[style.display]': "props?.fullWidth ? 'block' : null",
|
|
2914
2960
|
'[style.width]': "props?.fullWidth ? '100%' : null",
|
|
2915
|
-
}, template: "<div class=\"flex flex-col h-full\" [class.is-fullwidth]=\"props.fullWidth\">\n @if (uiConfig.label || props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ uiConfig.label || props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <input\n #dateInput\n [id]=\"resolvedId\"\n [attr.name]=\"resolvedName\"\n [type]=\"resolvedMode\"\n [attr.placeholder]=\"uiConfig.placeholder || props.placeholder || null\"\n [attr.aria-label]=\"
|
|
2961
|
+
}, template: "<div class=\"flex flex-col h-full\" [class.is-fullwidth]=\"props.fullWidth\">\n @if (uiConfig.label || props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ uiConfig.label || props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n @if (iconOnly) {\n <!--\n Icon-only variant: a bordered box holding a calendar icon, with the native\n input layered on top at zero opacity. The box carries the look (border,\n background, radius, hover, focus ring); the input stays a real, focusable\n form control that opens the picker on click. `focus-within` on the box\n surfaces the keyboard focus ring, since the input itself is transparent.\n -->\n <div [ngClass]=\"iconBoxClasses\">\n <svg [size]=\"iconSize\" lucideCalendarDays aria-hidden=\"true\" class=\"pointer-events-none\"></svg>\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n }\n\n @if (showError) {\n @if (props.showAllErrors) {\n <div class=\"flex flex-col gap-y-1\">\n @for (error of errorMessages; track $index) {\n <mn-error-message [errorMessage]=\"error\" [id]=\"resolvedId + '-' + $index\"></mn-error-message>\n }\n </div>\n } @else {\n @if (errorMessage !== null) {\n <mn-error-message [errorMessage]=\"errorMessage\" [id]=\"resolvedId\"></mn-error-message>\n }\n }\n }\n</div>\n\n<!-- Single input definition, rendered either bare or inside the icon box above,\n so both variants share exactly the same bindings and behaviour. -->\n<ng-template #inputTemplate>\n <input\n #dateInput\n [id]=\"resolvedId\"\n [attr.name]=\"resolvedName\"\n [type]=\"resolvedMode\"\n [attr.placeholder]=\"uiConfig.placeholder || props.placeholder || null\"\n [attr.aria-label]=\"resolvedAriaLabel\"\n [attr.aria-invalid]=\"showError || null\"\n [attr.aria-describedby]=\"showError ? resolvedId + '-error' : null\"\n [disabled]=\"isDisabled\"\n [attr.min]=\"props.min || null\"\n [attr.max]=\"props.max || null\"\n [attr.step]=\"props.step || null\"\n [value]=\"value ?? ''\"\n [ngClass]=\"inputClasses\"\n (click)=\"handleClick(dateInput)\"\n (input)=\"handleInput(($any($event.target)).value)\"\n (blur)=\"handleBlur()\"\n />\n</ng-template>\n", styles: ["input::-webkit-calendar-picker-indicator{cursor:pointer}@media(pointer:coarse){input[type=date],input[type=datetime-local],input[type=time],input[type=month],input[type=week]{-webkit-appearance:none;appearance:none;min-width:0;box-sizing:border-box}}\n"] }]
|
|
2916
2962
|
}], ctorParameters: () => [], propDecorators: { props: [{
|
|
2917
2963
|
type: Input,
|
|
2918
2964
|
args: [{ required: true }]
|
|
@@ -8075,6 +8121,7 @@ const DEFAULT_CALENDAR_CONFIG = (() => {
|
|
|
8075
8121
|
nextLabel: 'Next',
|
|
8076
8122
|
upcomingEventsTitle: 'Upcoming events',
|
|
8077
8123
|
noUpcomingEvents: 'No upcoming events',
|
|
8124
|
+
moreEventsLabel: 'more',
|
|
8078
8125
|
viewLabels: { MONTH: 'Month', WEEK: 'Week', DAY: 'Day' },
|
|
8079
8126
|
shortDayNames: names.short,
|
|
8080
8127
|
longDayNames: names.long,
|
|
@@ -8239,17 +8286,21 @@ class CalendarMonthComponent {
|
|
|
8239
8286
|
/** Emits the date of a clicked day cell. */
|
|
8240
8287
|
dayClicked = new EventEmitter();
|
|
8241
8288
|
monthItems = [];
|
|
8242
|
-
|
|
8289
|
+
/** Short weekday column headers (e.g. "Mon"), kept compact for the narrow columns. */
|
|
8290
|
+
weekdayLabels;
|
|
8291
|
+
/** Word shown after the "+N" overflow count (e.g. "more"), from config. */
|
|
8292
|
+
moreEventsLabel = DEFAULT_CALENDAR_CONFIG.moreEventsLabel;
|
|
8243
8293
|
events = [];
|
|
8244
8294
|
destroy$ = new Subject();
|
|
8245
8295
|
formatter;
|
|
8246
8296
|
constructor() {
|
|
8247
8297
|
this.formatter = new DefaultCalendarDateFormatter();
|
|
8248
|
-
this.
|
|
8298
|
+
this.weekdayLabels = DEFAULT_CALENDAR_CONFIG.shortDayNames;
|
|
8249
8299
|
}
|
|
8250
8300
|
ngOnInit() {
|
|
8251
8301
|
const resolved = this.config ? resolveCalendarConfig(this.config) : { ...DEFAULT_CALENDAR_CONFIG };
|
|
8252
|
-
this.
|
|
8302
|
+
this.weekdayLabels = resolved.shortDayNames;
|
|
8303
|
+
this.moreEventsLabel = resolved.moreEventsLabel;
|
|
8253
8304
|
this.buildMonth();
|
|
8254
8305
|
if (this.eventsChanged) {
|
|
8255
8306
|
this.eventsChanged.pipe(takeUntil(this.destroy$)).subscribe(events => {
|
|
@@ -8325,11 +8376,11 @@ class CalendarMonthComponent {
|
|
|
8325
8376
|
};
|
|
8326
8377
|
}
|
|
8327
8378
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarMonthComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8328
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarMonthComponent, isStandalone: true, selector: "mn-calendar-month", inputs: { focusDay: "focusDay", eventsChanged: "eventsChanged", focusDayChanged: "focusDayChanged", config: "config" }, outputs: { dayClicked: "dayClicked" }, ngImport: i0, template: "<div class=\"
|
|
8379
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarMonthComponent, isStandalone: true, selector: "mn-calendar-month", inputs: { focusDay: "focusDay", eventsChanged: "eventsChanged", focusDayChanged: "focusDayChanged", config: "config" }, outputs: { dayClicked: "dayClicked" }, ngImport: i0, template: "<!-- Month grid. Cells are grouped by space rather than boxed by borders, in\n keeping with the date-selector-bar: no rules, soft rounded cells, a hover\n wash, and today marked by a tinted number alone (no circle). -->\n<!-- Rows are a compact fixed height rather than stretched to fill the container:\n a month with few events shouldn't leave each cell mostly empty. The panel\n scrolls if the grid is taller than the space it's given. -->\n<div class=\"w-full flex flex-col\" role=\"grid\" aria-label=\"Month view\">\n <div class=\"grid grid-cols-7 pb-2\">\n @for (day of weekdayLabels; track day) {\n <div class=\"pl-2 text-left text-[11px] font-semibold uppercase tracking-wide opacity-45\" role=\"columnheader\">{{ day }}</div>\n }\n </div>\n <div class=\"grid grid-cols-7 gap-1 auto-rows-[minmax(92px,auto)]\">\n @for (item of monthItems; track item.date.getTime()) {\n <div\n class=\"flex flex-col gap-1 min-h-0 overflow-hidden rounded-xl p-1.5 cursor-pointer transition-colors hover:bg-base-200 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-primary motion-reduce:transition-none\"\n [class.opacity-40]=\"!item.isCurrentMonth\"\n (keyup.enter)=\"onDayClick(item.date)\"\n (click)=\"onDayClick(item.date)\"\n tabindex=\"0\"\n role=\"gridcell\"\n [attr.aria-label]=\"item.date.toDateString()\">\n <span class=\"text-[13px] font-bold leading-none tabular-nums\" [class.text-primary]=\"item.isToday\">{{ item.dayNumber }}</span>\n <div class=\"flex flex-col gap-0.5 min-h-0\">\n @for (event of item.events.slice(0, 3); track $index) {\n <div\n class=\"flex items-center overflow-hidden rounded-md border-l-[3px] px-1.5 py-0.5 text-[11px] leading-tight\"\n [style.background-color]=\"event.color.secondaryColor\"\n [style.border-left-color]=\"event.color.primaryColor\"\n [style.color]=\"event.color.primaryColor\"\n [title]=\"event.title\">\n <span class=\"truncate font-semibold\">{{ event.title }}</span>\n </div>\n }\n <!-- Overflow indicator. Styled as a pill that lights up on hover so it\n reads as actionable; the whole cell already navigates to the Day\n view for this date on click / Enter, where every event is listed. -->\n @if (item.events.length > 3) {\n <span class=\"mt-0.5 inline-flex w-fit items-center rounded-md px-1.5 py-0.5 text-[10.5px] font-semibold opacity-60 transition-colors hover:bg-base-300 hover:opacity-100 motion-reduce:transition-none\">\n +{{ item.events.length - 3 }} {{ moreEventsLabel }}\n </span>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
8329
8380
|
}
|
|
8330
8381
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarMonthComponent, decorators: [{
|
|
8331
8382
|
type: Component,
|
|
8332
|
-
args: [{ selector: 'mn-calendar-month', standalone: true, imports: [CommonModule], template: "<div class=\"
|
|
8383
|
+
args: [{ selector: 'mn-calendar-month', standalone: true, imports: [CommonModule], template: "<!-- Month grid. Cells are grouped by space rather than boxed by borders, in\n keeping with the date-selector-bar: no rules, soft rounded cells, a hover\n wash, and today marked by a tinted number alone (no circle). -->\n<!-- Rows are a compact fixed height rather than stretched to fill the container:\n a month with few events shouldn't leave each cell mostly empty. The panel\n scrolls if the grid is taller than the space it's given. -->\n<div class=\"w-full flex flex-col\" role=\"grid\" aria-label=\"Month view\">\n <div class=\"grid grid-cols-7 pb-2\">\n @for (day of weekdayLabels; track day) {\n <div class=\"pl-2 text-left text-[11px] font-semibold uppercase tracking-wide opacity-45\" role=\"columnheader\">{{ day }}</div>\n }\n </div>\n <div class=\"grid grid-cols-7 gap-1 auto-rows-[minmax(92px,auto)]\">\n @for (item of monthItems; track item.date.getTime()) {\n <div\n class=\"flex flex-col gap-1 min-h-0 overflow-hidden rounded-xl p-1.5 cursor-pointer transition-colors hover:bg-base-200 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-primary motion-reduce:transition-none\"\n [class.opacity-40]=\"!item.isCurrentMonth\"\n (keyup.enter)=\"onDayClick(item.date)\"\n (click)=\"onDayClick(item.date)\"\n tabindex=\"0\"\n role=\"gridcell\"\n [attr.aria-label]=\"item.date.toDateString()\">\n <span class=\"text-[13px] font-bold leading-none tabular-nums\" [class.text-primary]=\"item.isToday\">{{ item.dayNumber }}</span>\n <div class=\"flex flex-col gap-0.5 min-h-0\">\n @for (event of item.events.slice(0, 3); track $index) {\n <div\n class=\"flex items-center overflow-hidden rounded-md border-l-[3px] px-1.5 py-0.5 text-[11px] leading-tight\"\n [style.background-color]=\"event.color.secondaryColor\"\n [style.border-left-color]=\"event.color.primaryColor\"\n [style.color]=\"event.color.primaryColor\"\n [title]=\"event.title\">\n <span class=\"truncate font-semibold\">{{ event.title }}</span>\n </div>\n }\n <!-- Overflow indicator. Styled as a pill that lights up on hover so it\n reads as actionable; the whole cell already navigates to the Day\n view for this date on click / Enter, where every event is listed. -->\n @if (item.events.length > 3) {\n <span class=\"mt-0.5 inline-flex w-fit items-center rounded-md px-1.5 py-0.5 text-[10.5px] font-semibold opacity-60 transition-colors hover:bg-base-300 hover:opacity-100 motion-reduce:transition-none\">\n +{{ item.events.length - 3 }} {{ moreEventsLabel }}\n </span>\n }\n </div>\n </div>\n }\n </div>\n</div>\n" }]
|
|
8333
8384
|
}], ctorParameters: () => [], propDecorators: { focusDay: [{
|
|
8334
8385
|
type: Input
|
|
8335
8386
|
}], eventsChanged: [{
|
|
@@ -8547,11 +8598,11 @@ class CalendarEventDefaultComponent {
|
|
|
8547
8598
|
}
|
|
8548
8599
|
}
|
|
8549
8600
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarEventDefaultComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8550
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarEventDefaultComponent, isStandalone: true, selector: "mn-calendar-event-default", ngImport: i0, template: "<div class=\"
|
|
8601
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarEventDefaultComponent, isStandalone: true, selector: "mn-calendar-event-default", ngImport: i0, template: "<!-- The shared event chip: a colored spine and a tinted fill from the event's own\n colours, so it reads the same in week, day and month. Text inherits the\n event's primary colour, keeping it legible on either theme. -->\n<div class=\"h-full overflow-hidden cursor-pointer rounded-lg border-l-[3px] px-2 py-1 text-xs\"\n [style.background-color]=\"event.color.secondaryColor\"\n [style.border-left-color]=\"event.color.primaryColor\"\n [style.color]=\"event.color.primaryColor\">\n <div class=\"truncate font-semibold\">{{ event.title }}</div>\n <div class=\"truncate text-[11px] opacity-85\">{{ formattedTime }}</div>\n @if (event.description) {\n <div class=\"truncate text-[11px] opacity-75\">{{ event.description }}</div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
8551
8602
|
}
|
|
8552
8603
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarEventDefaultComponent, decorators: [{
|
|
8553
8604
|
type: Component,
|
|
8554
|
-
args: [{ selector: 'mn-calendar-event-default', standalone: true, imports: [CommonModule], template: "<div class=\"
|
|
8605
|
+
args: [{ selector: 'mn-calendar-event-default', standalone: true, imports: [CommonModule], template: "<!-- The shared event chip: a colored spine and a tinted fill from the event's own\n colours, so it reads the same in week, day and month. Text inherits the\n event's primary colour, keeping it legible on either theme. -->\n<div class=\"h-full overflow-hidden cursor-pointer rounded-lg border-l-[3px] px-2 py-1 text-xs\"\n [style.background-color]=\"event.color.secondaryColor\"\n [style.border-left-color]=\"event.color.primaryColor\"\n [style.color]=\"event.color.primaryColor\">\n <div class=\"truncate font-semibold\">{{ event.title }}</div>\n <div class=\"truncate text-[11px] opacity-85\">{{ formattedTime }}</div>\n @if (event.description) {\n <div class=\"truncate text-[11px] opacity-75\">{{ event.description }}</div>\n }\n</div>\n" }]
|
|
8555
8606
|
}], ctorParameters: () => [] });
|
|
8556
8607
|
|
|
8557
8608
|
/**
|
|
@@ -8596,11 +8647,11 @@ class CalendarEventComponent {
|
|
|
8596
8647
|
this.rendered = true;
|
|
8597
8648
|
}
|
|
8598
8649
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarEventComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8599
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: CalendarEventComponent, isStandalone: true, selector: "mn-calendar-event", inputs: { event: "event", customComponent: "customComponent" }, outputs: { eventClicked: "eventClicked" }, viewQueries: [{ propertyName: "eventContainer", first: true, predicate: ["eventContainer"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: "
|
|
8650
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: CalendarEventComponent, isStandalone: true, selector: "mn-calendar-event", inputs: { event: "event", customComponent: "customComponent" }, outputs: { eventClicked: "eventClicked" }, viewQueries: [{ propertyName: "eventContainer", first: true, predicate: ["eventContainer"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Host for the dynamically-rendered event component. Click/keyboard handling and\n the focus ring live on the week/day event wrapper, so this stays presentational\n to avoid a second, redundant tab stop per event. -->\n<div class=\"h-full w-full\">\n <ng-template #eventContainer></ng-template>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
8600
8651
|
}
|
|
8601
8652
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarEventComponent, decorators: [{
|
|
8602
8653
|
type: Component,
|
|
8603
|
-
args: [{ selector: 'mn-calendar-event', standalone: true, imports: [CommonModule], template: "
|
|
8654
|
+
args: [{ selector: 'mn-calendar-event', standalone: true, imports: [CommonModule], template: "<!-- Host for the dynamically-rendered event component. Click/keyboard handling and\n the focus ring live on the week/day event wrapper, so this stays presentational\n to avoid a second, redundant tab stop per event. -->\n<div class=\"h-full w-full\">\n <ng-template #eventContainer></ng-template>\n</div>\n" }]
|
|
8604
8655
|
}], propDecorators: { event: [{
|
|
8605
8656
|
type: Input
|
|
8606
8657
|
}], customComponent: [{
|
|
@@ -8639,6 +8690,8 @@ class CalendarWeekComponent {
|
|
|
8639
8690
|
totalRows = 0;
|
|
8640
8691
|
currentTimeRow = 0;
|
|
8641
8692
|
currentTimeCol = '';
|
|
8693
|
+
/** The current time, formatted for the label riding the now-line. */
|
|
8694
|
+
currentTimeLabel = '';
|
|
8642
8695
|
gridTemplateColumns = 'repeat(7, 1fr)';
|
|
8643
8696
|
dayColumnMap = [];
|
|
8644
8697
|
events = [];
|
|
@@ -8807,18 +8860,24 @@ class CalendarWeekComponent {
|
|
|
8807
8860
|
const dayInfo = this.dayColumnMap[dayIdx];
|
|
8808
8861
|
this.currentTimeCol = `${dayInfo.startCol} / span ${dayInfo.subColumns}`;
|
|
8809
8862
|
this.currentTimeRow = CalendarUtility.getCorrectRow(now.getHours(), now.getMinutes(), this.resolvedConfig.startHour);
|
|
8863
|
+
// formatTime is async; refresh the label and re-render when it resolves.
|
|
8864
|
+
this.formatter.formatTime(now).then(label => {
|
|
8865
|
+
this.currentTimeLabel = label;
|
|
8866
|
+
this.cdr.markForCheck();
|
|
8867
|
+
});
|
|
8810
8868
|
}
|
|
8811
8869
|
else {
|
|
8812
8870
|
this.currentTimeCol = '';
|
|
8813
8871
|
this.currentTimeRow = 0;
|
|
8872
|
+
this.currentTimeLabel = '';
|
|
8814
8873
|
}
|
|
8815
8874
|
}
|
|
8816
8875
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarWeekComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8817
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarWeekComponent, isStandalone: true, selector: "mn-calendar-week", inputs: { focusDay: "focusDay", eventsChanged: "eventsChanged", focusDayChanged: "focusDayChanged", config: "config", calendarEventComponent: "calendarEventComponent" }, outputs: { eventClicked: "eventClicked" }, providers: [CalendarEventLayoutService], ngImport: i0, template: "<div class=\"
|
|
8876
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarWeekComponent, isStandalone: true, selector: "mn-calendar-week", inputs: { focusDay: "focusDay", eventsChanged: "eventsChanged", focusDayChanged: "focusDayChanged", config: "config", calendarEventComponent: "calendarEventComponent" }, outputs: { eventClicked: "eventClicked" }, providers: [CalendarEventLayoutService], ngImport: i0, template: "<!-- Week grid. Hour rules are quiet hairlines; today's column header is a tinted\n number (no fill); the current time is a line carrying a small time bubble.\n Dynamic row/column placement stays inline \u2014 the layout maths lives in TS. -->\n<div class=\"w-full h-full flex flex-col overflow-hidden\" role=\"grid\" aria-label=\"Week view\">\n <div class=\"grid\" [style.grid-template-columns]=\"'60px ' + gridTemplateColumns\">\n <div></div>\n @for (col of columns; track col.dayName; let i = $index) {\n <div class=\"py-2 px-1 text-center\"\n [style.grid-column]=\"getHeaderColumn(i)\"\n role=\"columnheader\">\n <span class=\"block text-[11px] font-semibold uppercase tracking-wide opacity-50\">{{ col.dayName }}</span>\n <span class=\"text-lg font-bold tabular-nums\" [class.text-primary]=\"col.isToday\">{{ col.dayNumber }}</span>\n </div>\n }\n </div>\n <div class=\"grid grid-cols-[60px_1fr] flex-1 min-h-0 overflow-hidden items-stretch\">\n <div class=\"grid h-full min-h-0\" [style.grid-template-rows]=\"'repeat(' + totalRows + ', minmax(1.5rem, 1fr))'\">\n @for (row of hourRows; track row.topRow) {\n <div class=\"flex items-start justify-end min-h-0 overflow-hidden pr-2 text-[11px] tabular-nums opacity-50\"\n [style.grid-row]=\"row.topRow + '/' + row.bottomRow\">\n {{ row.hourLabel }}\n </div>\n }\n </div>\n <div class=\"grid relative auto-rows-fr h-full min-h-0\"\n [style.grid-template-rows]=\"'repeat(' + totalRows + ', minmax(1.5rem, 1fr))'\"\n [style.grid-template-columns]=\"gridTemplateColumns\">\n @for (row of hourRows; track row.topRow) {\n <div class=\"border-t border-base-200 pointer-events-none min-h-0\"\n [style.grid-row]=\"row.topRow + '/' + row.bottomRow\"\n [style.grid-column]=\"'1 / -1'\">\n </div>\n }\n @if (currentTimeRow > 0 && currentTimeCol) {\n <div class=\"relative z-[2] pointer-events-none\"\n [style.grid-row]=\"currentTimeRow\"\n [style.grid-column]=\"currentTimeCol\">\n <div class=\"absolute -left-1 -top-1 h-2 w-2 rounded-full bg-error\"></div>\n <div class=\"h-0.5 w-full bg-error\"></div>\n <div class=\"absolute left-0 top-0 -translate-x-2 -translate-y-1/2 rounded-full bg-error px-1.5 py-0.5 text-[10px] font-bold tabular-nums text-error-content shadow\">{{ currentTimeLabel }}</div>\n </div>\n }\n @for (event of displayEvents; track $index) {\n <div class=\"z-[1] min-h-0 overflow-hidden rounded-lg p-0.5 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary\"\n [style.grid-row]=\"getEventRow(event)\"\n [style.grid-column]=\"getEventColumn(event)\"\n (click)=\"onEventClick(event)\"\n (keyup.enter)=\"onEventClick(event)\"\n tabindex=\"0\">\n <mn-calendar-event [customComponent]=\"calendarEventComponent\" [event]=\"event\"></mn-calendar-event>\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CalendarEventComponent, selector: "mn-calendar-event", inputs: ["event", "customComponent"], outputs: ["eventClicked"] }] });
|
|
8818
8877
|
}
|
|
8819
8878
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarWeekComponent, decorators: [{
|
|
8820
8879
|
type: Component,
|
|
8821
|
-
args: [{ selector: 'mn-calendar-week', standalone: true, imports: [CommonModule, CalendarEventComponent], providers: [CalendarEventLayoutService], template: "<div class=\"
|
|
8880
|
+
args: [{ selector: 'mn-calendar-week', standalone: true, imports: [CommonModule, CalendarEventComponent], providers: [CalendarEventLayoutService], template: "<!-- Week grid. Hour rules are quiet hairlines; today's column header is a tinted\n number (no fill); the current time is a line carrying a small time bubble.\n Dynamic row/column placement stays inline \u2014 the layout maths lives in TS. -->\n<div class=\"w-full h-full flex flex-col overflow-hidden\" role=\"grid\" aria-label=\"Week view\">\n <div class=\"grid\" [style.grid-template-columns]=\"'60px ' + gridTemplateColumns\">\n <div></div>\n @for (col of columns; track col.dayName; let i = $index) {\n <div class=\"py-2 px-1 text-center\"\n [style.grid-column]=\"getHeaderColumn(i)\"\n role=\"columnheader\">\n <span class=\"block text-[11px] font-semibold uppercase tracking-wide opacity-50\">{{ col.dayName }}</span>\n <span class=\"text-lg font-bold tabular-nums\" [class.text-primary]=\"col.isToday\">{{ col.dayNumber }}</span>\n </div>\n }\n </div>\n <div class=\"grid grid-cols-[60px_1fr] flex-1 min-h-0 overflow-hidden items-stretch\">\n <div class=\"grid h-full min-h-0\" [style.grid-template-rows]=\"'repeat(' + totalRows + ', minmax(1.5rem, 1fr))'\">\n @for (row of hourRows; track row.topRow) {\n <div class=\"flex items-start justify-end min-h-0 overflow-hidden pr-2 text-[11px] tabular-nums opacity-50\"\n [style.grid-row]=\"row.topRow + '/' + row.bottomRow\">\n {{ row.hourLabel }}\n </div>\n }\n </div>\n <div class=\"grid relative auto-rows-fr h-full min-h-0\"\n [style.grid-template-rows]=\"'repeat(' + totalRows + ', minmax(1.5rem, 1fr))'\"\n [style.grid-template-columns]=\"gridTemplateColumns\">\n @for (row of hourRows; track row.topRow) {\n <div class=\"border-t border-base-200 pointer-events-none min-h-0\"\n [style.grid-row]=\"row.topRow + '/' + row.bottomRow\"\n [style.grid-column]=\"'1 / -1'\">\n </div>\n }\n @if (currentTimeRow > 0 && currentTimeCol) {\n <div class=\"relative z-[2] pointer-events-none\"\n [style.grid-row]=\"currentTimeRow\"\n [style.grid-column]=\"currentTimeCol\">\n <div class=\"absolute -left-1 -top-1 h-2 w-2 rounded-full bg-error\"></div>\n <div class=\"h-0.5 w-full bg-error\"></div>\n <div class=\"absolute left-0 top-0 -translate-x-2 -translate-y-1/2 rounded-full bg-error px-1.5 py-0.5 text-[10px] font-bold tabular-nums text-error-content shadow\">{{ currentTimeLabel }}</div>\n </div>\n }\n @for (event of displayEvents; track $index) {\n <div class=\"z-[1] min-h-0 overflow-hidden rounded-lg p-0.5 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary\"\n [style.grid-row]=\"getEventRow(event)\"\n [style.grid-column]=\"getEventColumn(event)\"\n (click)=\"onEventClick(event)\"\n (keyup.enter)=\"onEventClick(event)\"\n tabindex=\"0\">\n <mn-calendar-event [customComponent]=\"calendarEventComponent\" [event]=\"event\"></mn-calendar-event>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
|
|
8822
8881
|
}], ctorParameters: () => [], propDecorators: { focusDay: [{
|
|
8823
8882
|
type: Input
|
|
8824
8883
|
}], eventsChanged: [{
|
|
@@ -8859,6 +8918,8 @@ class CalendarDayComponent {
|
|
|
8859
8918
|
totalRows = 0;
|
|
8860
8919
|
totalColumns = 1;
|
|
8861
8920
|
currentTimeRow = 0;
|
|
8921
|
+
/** The current time, formatted for the label riding the now-line. */
|
|
8922
|
+
currentTimeLabel = '';
|
|
8862
8923
|
isToday = false;
|
|
8863
8924
|
dayName = '';
|
|
8864
8925
|
events = [];
|
|
@@ -8962,8 +9023,7 @@ class CalendarDayComponent {
|
|
|
8962
9023
|
this.displayEvents = this.layoutService.calculateMultiDayEvents(filtered, this.resolvedConfig.startHour, this.resolvedConfig.endHour, rangeStart, rangeEnd);
|
|
8963
9024
|
this.layoutService.assignColumnsToEvents(this.displayEvents);
|
|
8964
9025
|
this.layoutService.assignWidthsToEvents(this.displayEvents, rangeStart, rangeEnd);
|
|
8965
|
-
|
|
8966
|
-
this.totalColumns = maxCol;
|
|
9026
|
+
this.totalColumns = this.displayEvents.reduce((max, e) => Math.max(max, (e.column ?? 0) + (e.width ?? 1)), 1);
|
|
8967
9027
|
}
|
|
8968
9028
|
/** Updates the current-time red line position. */
|
|
8969
9029
|
updateCurrentTime() {
|
|
@@ -8971,18 +9031,24 @@ class CalendarDayComponent {
|
|
|
8971
9031
|
if (this.focusDay && this.formatter.isSameDay(this.focusDay, now)) {
|
|
8972
9032
|
this.currentTimeRow = CalendarUtility.getCorrectRow(now.getHours(), now.getMinutes(), this.resolvedConfig.startHour);
|
|
8973
9033
|
this.isToday = true;
|
|
9034
|
+
// formatTime is async; refresh the label and re-render when it resolves.
|
|
9035
|
+
this.formatter.formatTime(now).then(label => {
|
|
9036
|
+
this.currentTimeLabel = label;
|
|
9037
|
+
this.cdr.markForCheck();
|
|
9038
|
+
});
|
|
8974
9039
|
}
|
|
8975
9040
|
else {
|
|
8976
9041
|
this.currentTimeRow = 0;
|
|
8977
9042
|
this.isToday = false;
|
|
9043
|
+
this.currentTimeLabel = '';
|
|
8978
9044
|
}
|
|
8979
9045
|
}
|
|
8980
9046
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarDayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8981
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarDayComponent, isStandalone: true, selector: "mn-calendar-day", inputs: { focusDay: "focusDay", eventsChanged: "eventsChanged", focusDayChanged: "focusDayChanged", config: "config", calendarEventComponent: "calendarEventComponent" }, outputs: { eventClicked: "eventClicked" }, providers: [CalendarEventLayoutService], ngImport: i0, template: "<div class=\"
|
|
9047
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarDayComponent, isStandalone: true, selector: "mn-calendar-day", inputs: { focusDay: "focusDay", eventsChanged: "eventsChanged", focusDayChanged: "focusDayChanged", config: "config", calendarEventComponent: "calendarEventComponent" }, outputs: { eventClicked: "eventClicked" }, providers: [CalendarEventLayoutService], ngImport: i0, template: "<!-- Day grid. One column, its header centered over that column (the toolbar\n already names the date). Today's number is tinted, not filled; the now-line\n carries a time bubble that overhangs into the gutter. -->\n<div class=\"w-full h-full flex flex-col overflow-hidden\" role=\"grid\" aria-label=\"Day view\">\n <div class=\"grid grid-cols-[60px_1fr]\">\n <div></div>\n <div class=\"flex items-center justify-center gap-2 py-2 px-1\" role=\"columnheader\">\n <span class=\"text-[11px] font-semibold uppercase tracking-wide opacity-50\">{{ dayName }}</span>\n <span class=\"text-base font-bold tabular-nums\" [class.text-primary]=\"isToday\">{{ focusDay.getDate() }}</span>\n </div>\n </div>\n <div class=\"grid grid-cols-[60px_1fr] flex-1 min-h-0 overflow-hidden items-stretch\">\n <div class=\"grid h-full min-h-0\" [style.grid-template-rows]=\"'repeat(' + totalRows + ', minmax(1.5rem, 1fr))'\">\n @for (row of hourRows; track row.topRow) {\n <div class=\"flex items-start justify-end min-h-0 overflow-hidden pr-2 text-[11px] tabular-nums opacity-50\"\n [style.grid-row]=\"row.topRow + '/' + row.bottomRow\">\n {{ row.hourLabel }}\n </div>\n }\n </div>\n <div class=\"grid relative auto-rows-fr h-full min-h-0\"\n [style.grid-template-rows]=\"'repeat(' + totalRows + ', minmax(1.5rem, 1fr))'\"\n [style.grid-template-columns]=\"'repeat(' + totalColumns + ', 1fr)'\">\n @for (row of hourRows; track row.topRow) {\n <div class=\"border-t border-base-200 pointer-events-none min-h-0\"\n [style.grid-row]=\"row.topRow + '/' + row.bottomRow\"\n [style.grid-column]=\"'1 / -1'\">\n </div>\n }\n @if (currentTimeRow > 0 && isToday) {\n <div class=\"relative z-[2] pointer-events-none\"\n [style.grid-row]=\"currentTimeRow\"\n [style.grid-column]=\"'1 / -1'\">\n <div class=\"absolute -left-1 -top-1 h-2 w-2 rounded-full bg-error\"></div>\n <div class=\"h-0.5 w-full bg-error\"></div>\n <div class=\"absolute left-0 top-0 -translate-x-2 -translate-y-1/2 rounded-full bg-error px-1.5 py-0.5 text-[10px] font-bold tabular-nums text-error-content shadow\">{{ currentTimeLabel }}</div>\n </div>\n }\n @for (event of displayEvents; track $index) {\n <div class=\"z-[1] min-h-0 overflow-hidden rounded-lg p-0.5 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary\"\n [style.grid-row]=\"getEventRow(event)\"\n [style.grid-column]=\"getEventColumn(event)\"\n (click)=\"onEventClick(event)\"\n (keyup.enter)=\"onEventClick(event)\"\n tabindex=\"0\">\n <mn-calendar-event [customComponent]=\"calendarEventComponent\" [event]=\"event\"></mn-calendar-event>\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CalendarEventComponent, selector: "mn-calendar-event", inputs: ["event", "customComponent"], outputs: ["eventClicked"] }] });
|
|
8982
9048
|
}
|
|
8983
9049
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarDayComponent, decorators: [{
|
|
8984
9050
|
type: Component,
|
|
8985
|
-
args: [{ selector: 'mn-calendar-day', standalone: true, imports: [CommonModule, CalendarEventComponent], providers: [CalendarEventLayoutService], template: "<div class=\"
|
|
9051
|
+
args: [{ selector: 'mn-calendar-day', standalone: true, imports: [CommonModule, CalendarEventComponent], providers: [CalendarEventLayoutService], template: "<!-- Day grid. One column, its header centered over that column (the toolbar\n already names the date). Today's number is tinted, not filled; the now-line\n carries a time bubble that overhangs into the gutter. -->\n<div class=\"w-full h-full flex flex-col overflow-hidden\" role=\"grid\" aria-label=\"Day view\">\n <div class=\"grid grid-cols-[60px_1fr]\">\n <div></div>\n <div class=\"flex items-center justify-center gap-2 py-2 px-1\" role=\"columnheader\">\n <span class=\"text-[11px] font-semibold uppercase tracking-wide opacity-50\">{{ dayName }}</span>\n <span class=\"text-base font-bold tabular-nums\" [class.text-primary]=\"isToday\">{{ focusDay.getDate() }}</span>\n </div>\n </div>\n <div class=\"grid grid-cols-[60px_1fr] flex-1 min-h-0 overflow-hidden items-stretch\">\n <div class=\"grid h-full min-h-0\" [style.grid-template-rows]=\"'repeat(' + totalRows + ', minmax(1.5rem, 1fr))'\">\n @for (row of hourRows; track row.topRow) {\n <div class=\"flex items-start justify-end min-h-0 overflow-hidden pr-2 text-[11px] tabular-nums opacity-50\"\n [style.grid-row]=\"row.topRow + '/' + row.bottomRow\">\n {{ row.hourLabel }}\n </div>\n }\n </div>\n <div class=\"grid relative auto-rows-fr h-full min-h-0\"\n [style.grid-template-rows]=\"'repeat(' + totalRows + ', minmax(1.5rem, 1fr))'\"\n [style.grid-template-columns]=\"'repeat(' + totalColumns + ', 1fr)'\">\n @for (row of hourRows; track row.topRow) {\n <div class=\"border-t border-base-200 pointer-events-none min-h-0\"\n [style.grid-row]=\"row.topRow + '/' + row.bottomRow\"\n [style.grid-column]=\"'1 / -1'\">\n </div>\n }\n @if (currentTimeRow > 0 && isToday) {\n <div class=\"relative z-[2] pointer-events-none\"\n [style.grid-row]=\"currentTimeRow\"\n [style.grid-column]=\"'1 / -1'\">\n <div class=\"absolute -left-1 -top-1 h-2 w-2 rounded-full bg-error\"></div>\n <div class=\"h-0.5 w-full bg-error\"></div>\n <div class=\"absolute left-0 top-0 -translate-x-2 -translate-y-1/2 rounded-full bg-error px-1.5 py-0.5 text-[10px] font-bold tabular-nums text-error-content shadow\">{{ currentTimeLabel }}</div>\n </div>\n }\n @for (event of displayEvents; track $index) {\n <div class=\"z-[1] min-h-0 overflow-hidden rounded-lg p-0.5 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary\"\n [style.grid-row]=\"getEventRow(event)\"\n [style.grid-column]=\"getEventColumn(event)\"\n (click)=\"onEventClick(event)\"\n (keyup.enter)=\"onEventClick(event)\"\n tabindex=\"0\">\n <mn-calendar-event [customComponent]=\"calendarEventComponent\" [event]=\"event\"></mn-calendar-event>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
|
|
8986
9052
|
}], ctorParameters: () => [], propDecorators: { focusDay: [{
|
|
8987
9053
|
type: Input
|
|
8988
9054
|
}], eventsChanged: [{
|
|
@@ -9020,11 +9086,11 @@ class UpcomingEventRowComponent {
|
|
|
9020
9086
|
}
|
|
9021
9087
|
}
|
|
9022
9088
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UpcomingEventRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9023
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: UpcomingEventRowComponent, isStandalone: true, selector: "mn-upcoming-event-row", inputs: { event: "event" }, outputs: { eventClicked: "eventClicked" }, ngImport: i0, template: "<div (click)=\"eventClicked.emit(event)\"
|
|
9089
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: UpcomingEventRowComponent, isStandalone: true, selector: "mn-upcoming-event-row", inputs: { event: "event" }, outputs: { eventClicked: "eventClicked" }, ngImport: i0, template: "<!-- Sidebar row in the shared chip language: a colored rail from the event's own\n colour, a hover wash, and space-grouped content \u2014 no boxed borders. -->\n<div (click)=\"eventClicked.emit(event)\"\n (keyup.enter)=\"eventClicked.emit(event)\"\n class=\"grid grid-cols-[3px_1fr] items-stretch gap-2.5 mb-1.5 cursor-pointer rounded-lg p-2 transition-colors hover:bg-base-200 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-primary motion-reduce:transition-none\"\n tabindex=\"0\">\n <div class=\"rounded-full\" [style.background-color]=\"event.color.primaryColor\"></div>\n <div class=\"min-w-0\">\n <div class=\"truncate text-[13px] font-semibold\">{{ event.title }}</div>\n <div class=\"text-xs tabular-nums opacity-60\">{{ formattedDate }}</div>\n @if (event.description) {\n <div class=\"truncate text-xs opacity-50\">{{ event.description }}</div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
9024
9090
|
}
|
|
9025
9091
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UpcomingEventRowComponent, decorators: [{
|
|
9026
9092
|
type: Component,
|
|
9027
|
-
args: [{ selector: 'mn-upcoming-event-row', standalone: true, imports: [CommonModule], template: "<div (click)=\"eventClicked.emit(event)\"
|
|
9093
|
+
args: [{ selector: 'mn-upcoming-event-row', standalone: true, imports: [CommonModule], template: "<!-- Sidebar row in the shared chip language: a colored rail from the event's own\n colour, a hover wash, and space-grouped content \u2014 no boxed borders. -->\n<div (click)=\"eventClicked.emit(event)\"\n (keyup.enter)=\"eventClicked.emit(event)\"\n class=\"grid grid-cols-[3px_1fr] items-stretch gap-2.5 mb-1.5 cursor-pointer rounded-lg p-2 transition-colors hover:bg-base-200 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-primary motion-reduce:transition-none\"\n tabindex=\"0\">\n <div class=\"rounded-full\" [style.background-color]=\"event.color.primaryColor\"></div>\n <div class=\"min-w-0\">\n <div class=\"truncate text-[13px] font-semibold\">{{ event.title }}</div>\n <div class=\"text-xs tabular-nums opacity-60\">{{ formattedDate }}</div>\n @if (event.description) {\n <div class=\"truncate text-xs opacity-50\">{{ event.description }}</div>\n }\n </div>\n</div>\n" }]
|
|
9028
9094
|
}], ctorParameters: () => [], propDecorators: { event: [{
|
|
9029
9095
|
type: Input
|
|
9030
9096
|
}], eventClicked: [{
|
|
@@ -9081,11 +9147,11 @@ class UpcomingEventsComponent {
|
|
|
9081
9147
|
return event.id;
|
|
9082
9148
|
}
|
|
9083
9149
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UpcomingEventsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9084
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: UpcomingEventsComponent, isStandalone: true, selector: "mn-upcoming-events", inputs: { eventsChanged: "eventsChanged", config: "config" }, outputs: { eventClicked: "eventClicked" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"
|
|
9150
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: UpcomingEventsComponent, isStandalone: true, selector: "mn-upcoming-events", inputs: { eventsChanged: "eventsChanged", config: "config" }, outputs: { eventClicked: "eventClicked" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"p-4\" role=\"complementary\" aria-label=\"Upcoming events\">\n <div class=\"mb-3 flex items-center gap-2\">\n <h3 class=\"text-sm font-bold tracking-wide\">{{ title }}</h3>\n @if (upcomingEvents.length) {\n <span class=\"text-xs font-semibold tabular-nums opacity-45\">{{ upcomingEvents.length }}</span>\n }\n </div>\n @for (event of upcomingEvents; track $index) {\n <mn-upcoming-event-row\n [event]=\"event\"\n (eventClicked)=\"eventClicked.emit($event)\">\n </mn-upcoming-event-row>\n }\n @if (upcomingEvents.length === 0) {\n <div class=\"text-sm opacity-50\">{{ noEventsMessage }}</div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: UpcomingEventRowComponent, selector: "mn-upcoming-event-row", inputs: ["event"], outputs: ["eventClicked"] }] });
|
|
9085
9151
|
}
|
|
9086
9152
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UpcomingEventsComponent, decorators: [{
|
|
9087
9153
|
type: Component,
|
|
9088
|
-
args: [{ selector: 'mn-upcoming-events', standalone: true, imports: [CommonModule, UpcomingEventRowComponent], template: "<div class=\"
|
|
9154
|
+
args: [{ selector: 'mn-upcoming-events', standalone: true, imports: [CommonModule, UpcomingEventRowComponent], template: "<div class=\"p-4\" role=\"complementary\" aria-label=\"Upcoming events\">\n <div class=\"mb-3 flex items-center gap-2\">\n <h3 class=\"text-sm font-bold tracking-wide\">{{ title }}</h3>\n @if (upcomingEvents.length) {\n <span class=\"text-xs font-semibold tabular-nums opacity-45\">{{ upcomingEvents.length }}</span>\n }\n </div>\n @for (event of upcomingEvents; track $index) {\n <mn-upcoming-event-row\n [event]=\"event\"\n (eventClicked)=\"eventClicked.emit($event)\">\n </mn-upcoming-event-row>\n }\n @if (upcomingEvents.length === 0) {\n <div class=\"text-sm opacity-50\">{{ noEventsMessage }}</div>\n }\n</div>\n" }]
|
|
9089
9155
|
}], ctorParameters: () => [], propDecorators: { eventsChanged: [{
|
|
9090
9156
|
type: Input
|
|
9091
9157
|
}], config: [{
|
|
@@ -9296,8 +9362,12 @@ class CalendarViewComponent {
|
|
|
9296
9362
|
checkMobileView() {
|
|
9297
9363
|
const wasMobile = this.isMobileView;
|
|
9298
9364
|
const width = window.innerWidth;
|
|
9299
|
-
|
|
9300
|
-
|
|
9365
|
+
// Honour the configured mobile breakpoint (default 768) rather than a hard-coded
|
|
9366
|
+
// width: a seven-column week is cramped well before a phone's width, so the day
|
|
9367
|
+
// view takes over earlier — and consumers can tune where via `mobileBreakpoint`.
|
|
9368
|
+
const breakpoint = this.config.mobileBreakpoint;
|
|
9369
|
+
this.isMobileView = width < breakpoint;
|
|
9370
|
+
this.isTabletView = width >= breakpoint && width < 1024;
|
|
9301
9371
|
if (this.isMobileView && !wasMobile) {
|
|
9302
9372
|
this.currentView = CalendarView.DAY;
|
|
9303
9373
|
}
|
|
@@ -9330,7 +9400,7 @@ class CalendarViewComponent {
|
|
|
9330
9400
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9331
9401
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CalendarViewComponent, isStandalone: true, selector: "mn-calendar-view", inputs: { showButton: "showButton", buttonTitle: "buttonTitle", buttons: "buttons", CalendarEventComponent: "CalendarEventComponent", NewCalendarItemsEvent: "NewCalendarItemsEvent" }, outputs: { RequestNewCalendarItemsEvent: "RequestNewCalendarItemsEvent", CalendarItemClickedEvent: "CalendarItemClickedEvent", ButtonClickedEvent: "ButtonClickedEvent" }, host: { listeners: { "window:resize": "onResize()" } }, providers: [
|
|
9332
9402
|
provideMnCalendarConfig(DEFAULT_CALENDAR_CONFIG),
|
|
9333
|
-
], ngImport: i0, template: "<div class=\"w-full h-full flex flex-col\" role=\"application\" aria-label=\"Calendar\">\n\n <!--
|
|
9403
|
+
], ngImport: i0, template: "<div class=\"w-full h-full flex flex-col\" role=\"application\" aria-label=\"Calendar\">\n\n <!-- Toolbar spans the full width so the primary action can sit above the\n upcoming-events sidebar, using that otherwise-empty space. On mobile it\n reflows: Today and the controls share the top row, and the \u2039 date \u203A nav\n drops to its own full-width row below. Responsive `order` does the reflow. -->\n <div class=\"flex flex-wrap items-center gap-x-3 gap-y-2 py-3\">\n\n <button (click)=\"goToToday()\"\n [data]=\"{ variant: 'outline', size: 'md', color: 'primary' }\"\n class=\"order-1 shrink-0\"\n mnButton\n type=\"button\">\n {{ config.todayLabel }}\n </button>\n\n <!-- Jumping somewhere far off, which the arrows would take all day to reach.\n On mobile the picker collapses to its icon-only variant. Its `ml-auto`\n pulls the picker \u2014 and the switcher and action button that follow it \u2014 to\n the right of the row on every width, leaving Today (and the nav) on the\n left, so the controls align right consistently across devices. -->\n <mn-lib-datetime (ngModelChange)=\"onPickDate($event)\"\n [ngModel]=\"focusDayString\"\n [props]=\"{ id: panelId + '-date', mode: 'date', placeholder: config.pickDateLabel, size: 'md', borderRadius: 'lg', hover: true, iconOnly: isMobileView }\"\n class=\"order-2 ml-auto block lg:order-3\"></mn-lib-datetime>\n\n <!-- View switcher (month / week / day) \u2014 hidden on mobile, which is forced to day view. -->\n @if (!isMobileView) {\n <div aria-label=\"Calendar view\" class=\"order-3 flex border border-base-300 rounded-md overflow-hidden lg:order-4\" role=\"tablist\">\n @for (view of viewOptions; track view.value) {\n <button\n (click)=\"switchView(view.value)\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-selected]=\"currentView === view.value\"\n [data]=\"{ size: 'md', variant: currentView === view.value ? 'fill' : 'text', color: 'primary' }\"\n mnButton\n role=\"tab\"\n type=\"button\">\n {{ view.label }}\n </button>\n }\n </div>\n }\n\n <!-- Action buttons (custom buttons + optional showButton CTA). They follow the\n picker, so the picker's `ml-auto` carries them to the right of the row on\n every width \u2014 on desktop that lands them above the upcoming-events sidebar. -->\n @if (buttons.length || showButton) {\n <div class=\"order-4 flex items-center gap-2 lg:order-5\">\n @for (btn of buttons; track btn.label) {\n <button (click)=\"btn.onClick()\" [data]=\"btn.buttonData || {}\" mnButton type=\"button\">\n {{ btn.label }}\n </button>\n }\n @if (showButton) {\n <button (click)=\"ButtonClickedEvent.emit()\" [data]=\"{}\" mnButton type=\"button\">\n {{ buttonTitle }}\n </button>\n }\n </div>\n }\n\n <!-- Stepping through time. The arrows sit either side of the date they move,\n so the control and the thing it controls read as one unit. On mobile this\n nav takes its own full-width row (basis-full, ordered last) with the date\n centred between the arrows at the row's edges; on desktop it sits inline\n after Today. `aria-live` announces the new period after each step. -->\n <div class=\"order-5 flex basis-full items-center justify-between gap-1 lg:order-2 lg:basis-auto lg:justify-start\">\n <button (click)=\"navigate(-1)\"\n [attr.aria-label]=\"config.previousLabel\"\n [data]=\"{ variant: 'text', size: 'md', color: 'gray' }\"\n class=\"shrink-0\"\n mnButton\n type=\"button\">\n <svg [size]=\"18\" lucideChevronLeft></svg>\n </button>\n\n <h2 aria-live=\"polite\" class=\"text-center text-base font-semibold whitespace-nowrap px-1 lg:min-w-56\">\n {{ periodLabel }}\n </h2>\n\n <button (click)=\"navigate(1)\"\n [attr.aria-label]=\"config.nextLabel\"\n [data]=\"{ variant: 'text', size: 'md', color: 'gray' }\"\n class=\"shrink-0\"\n mnButton\n type=\"button\">\n <svg [size]=\"18\" lucideChevronRight></svg>\n </button>\n </div>\n\n </div>\n\n <div class=\"grid grid-cols-1 lg:grid-cols-[1fr_220px] gap-3 flex-1 min-h-0\">\n <div [id]=\"panelId\" class=\"min-w-0 min-h-0 overflow-hidden overflow-y-auto\" role=\"tabpanel\">\n @if (currentView === CalendarView.MONTH) {\n <mn-calendar-month\n [focusDay]=\"focusDay\"\n [eventsChanged]=\"internalEventsChanged\"\n [focusDayChanged]=\"internalFocusDayChanged\"\n [config]=\"config\"\n (dayClicked)=\"onMonthDayClick($event)\">\n </mn-calendar-month>\n }\n @if (currentView === CalendarView.WEEK) {\n <mn-calendar-week\n [focusDay]=\"focusDay\"\n [eventsChanged]=\"internalEventsChanged\"\n [focusDayChanged]=\"internalFocusDayChanged\"\n [config]=\"config\"\n [calendarEventComponent]=\"CalendarEventComponent\"\n (eventClicked)=\"onEventClick($event)\">\n </mn-calendar-week>\n }\n @if (currentView === CalendarView.DAY) {\n <mn-calendar-day\n [focusDay]=\"focusDay\"\n [eventsChanged]=\"internalEventsChanged\"\n [focusDayChanged]=\"internalFocusDayChanged\"\n [config]=\"config\"\n [calendarEventComponent]=\"CalendarEventComponent\"\n (eventClicked)=\"onEventClick($event)\">\n </mn-calendar-day>\n }\n </div>\n <div class=\"hidden lg:block border-l border-base-300 overflow-auto\">\n <mn-upcoming-events\n [eventsChanged]=\"internalEventsChanged\"\n [config]=\"config\"\n (eventClicked)=\"onEventClick($event)\">\n </mn-upcoming-events>\n </div>\n </div>\n\n</div>\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CalendarMonthComponent, selector: "mn-calendar-month", inputs: ["focusDay", "eventsChanged", "focusDayChanged", "config"], outputs: ["dayClicked"] }, { kind: "component", type: CalendarWeekComponent, selector: "mn-calendar-week", inputs: ["focusDay", "eventsChanged", "focusDayChanged", "config", "calendarEventComponent"], outputs: ["eventClicked"] }, { kind: "component", type: CalendarDayComponent, selector: "mn-calendar-day", inputs: ["focusDay", "eventsChanged", "focusDayChanged", "config", "calendarEventComponent"], outputs: ["eventClicked"] }, { kind: "component", type: UpcomingEventsComponent, selector: "mn-upcoming-events", inputs: ["eventsChanged", "config"], outputs: ["eventClicked"] }, { kind: "component", type: MnButton, selector: "button[mnButton], a[mnButton]", inputs: ["data"] }, { kind: "component", type: MnDatetime, selector: "mn-lib-datetime", inputs: ["props"] }, { kind: "component", type: LucideChevronLeft, selector: "svg[lucideChevronLeft]" }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }] });
|
|
9334
9404
|
}
|
|
9335
9405
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarViewComponent, decorators: [{
|
|
9336
9406
|
type: Component,
|
|
@@ -9347,7 +9417,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
9347
9417
|
LucideChevronRight
|
|
9348
9418
|
], providers: [
|
|
9349
9419
|
provideMnCalendarConfig(DEFAULT_CALENDAR_CONFIG),
|
|
9350
|
-
], template: "<div class=\"w-full h-full flex flex-col\" role=\"application\" aria-label=\"Calendar\">\n\n <!--
|
|
9420
|
+
], template: "<div class=\"w-full h-full flex flex-col\" role=\"application\" aria-label=\"Calendar\">\n\n <!-- Toolbar spans the full width so the primary action can sit above the\n upcoming-events sidebar, using that otherwise-empty space. On mobile it\n reflows: Today and the controls share the top row, and the \u2039 date \u203A nav\n drops to its own full-width row below. Responsive `order` does the reflow. -->\n <div class=\"flex flex-wrap items-center gap-x-3 gap-y-2 py-3\">\n\n <button (click)=\"goToToday()\"\n [data]=\"{ variant: 'outline', size: 'md', color: 'primary' }\"\n class=\"order-1 shrink-0\"\n mnButton\n type=\"button\">\n {{ config.todayLabel }}\n </button>\n\n <!-- Jumping somewhere far off, which the arrows would take all day to reach.\n On mobile the picker collapses to its icon-only variant. Its `ml-auto`\n pulls the picker \u2014 and the switcher and action button that follow it \u2014 to\n the right of the row on every width, leaving Today (and the nav) on the\n left, so the controls align right consistently across devices. -->\n <mn-lib-datetime (ngModelChange)=\"onPickDate($event)\"\n [ngModel]=\"focusDayString\"\n [props]=\"{ id: panelId + '-date', mode: 'date', placeholder: config.pickDateLabel, size: 'md', borderRadius: 'lg', hover: true, iconOnly: isMobileView }\"\n class=\"order-2 ml-auto block lg:order-3\"></mn-lib-datetime>\n\n <!-- View switcher (month / week / day) \u2014 hidden on mobile, which is forced to day view. -->\n @if (!isMobileView) {\n <div aria-label=\"Calendar view\" class=\"order-3 flex border border-base-300 rounded-md overflow-hidden lg:order-4\" role=\"tablist\">\n @for (view of viewOptions; track view.value) {\n <button\n (click)=\"switchView(view.value)\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-selected]=\"currentView === view.value\"\n [data]=\"{ size: 'md', variant: currentView === view.value ? 'fill' : 'text', color: 'primary' }\"\n mnButton\n role=\"tab\"\n type=\"button\">\n {{ view.label }}\n </button>\n }\n </div>\n }\n\n <!-- Action buttons (custom buttons + optional showButton CTA). They follow the\n picker, so the picker's `ml-auto` carries them to the right of the row on\n every width \u2014 on desktop that lands them above the upcoming-events sidebar. -->\n @if (buttons.length || showButton) {\n <div class=\"order-4 flex items-center gap-2 lg:order-5\">\n @for (btn of buttons; track btn.label) {\n <button (click)=\"btn.onClick()\" [data]=\"btn.buttonData || {}\" mnButton type=\"button\">\n {{ btn.label }}\n </button>\n }\n @if (showButton) {\n <button (click)=\"ButtonClickedEvent.emit()\" [data]=\"{}\" mnButton type=\"button\">\n {{ buttonTitle }}\n </button>\n }\n </div>\n }\n\n <!-- Stepping through time. The arrows sit either side of the date they move,\n so the control and the thing it controls read as one unit. On mobile this\n nav takes its own full-width row (basis-full, ordered last) with the date\n centred between the arrows at the row's edges; on desktop it sits inline\n after Today. `aria-live` announces the new period after each step. -->\n <div class=\"order-5 flex basis-full items-center justify-between gap-1 lg:order-2 lg:basis-auto lg:justify-start\">\n <button (click)=\"navigate(-1)\"\n [attr.aria-label]=\"config.previousLabel\"\n [data]=\"{ variant: 'text', size: 'md', color: 'gray' }\"\n class=\"shrink-0\"\n mnButton\n type=\"button\">\n <svg [size]=\"18\" lucideChevronLeft></svg>\n </button>\n\n <h2 aria-live=\"polite\" class=\"text-center text-base font-semibold whitespace-nowrap px-1 lg:min-w-56\">\n {{ periodLabel }}\n </h2>\n\n <button (click)=\"navigate(1)\"\n [attr.aria-label]=\"config.nextLabel\"\n [data]=\"{ variant: 'text', size: 'md', color: 'gray' }\"\n class=\"shrink-0\"\n mnButton\n type=\"button\">\n <svg [size]=\"18\" lucideChevronRight></svg>\n </button>\n </div>\n\n </div>\n\n <div class=\"grid grid-cols-1 lg:grid-cols-[1fr_220px] gap-3 flex-1 min-h-0\">\n <div [id]=\"panelId\" class=\"min-w-0 min-h-0 overflow-hidden overflow-y-auto\" role=\"tabpanel\">\n @if (currentView === CalendarView.MONTH) {\n <mn-calendar-month\n [focusDay]=\"focusDay\"\n [eventsChanged]=\"internalEventsChanged\"\n [focusDayChanged]=\"internalFocusDayChanged\"\n [config]=\"config\"\n (dayClicked)=\"onMonthDayClick($event)\">\n </mn-calendar-month>\n }\n @if (currentView === CalendarView.WEEK) {\n <mn-calendar-week\n [focusDay]=\"focusDay\"\n [eventsChanged]=\"internalEventsChanged\"\n [focusDayChanged]=\"internalFocusDayChanged\"\n [config]=\"config\"\n [calendarEventComponent]=\"CalendarEventComponent\"\n (eventClicked)=\"onEventClick($event)\">\n </mn-calendar-week>\n }\n @if (currentView === CalendarView.DAY) {\n <mn-calendar-day\n [focusDay]=\"focusDay\"\n [eventsChanged]=\"internalEventsChanged\"\n [focusDayChanged]=\"internalFocusDayChanged\"\n [config]=\"config\"\n [calendarEventComponent]=\"CalendarEventComponent\"\n (eventClicked)=\"onEventClick($event)\">\n </mn-calendar-day>\n }\n </div>\n <div class=\"hidden lg:block border-l border-base-300 overflow-auto\">\n <mn-upcoming-events\n [eventsChanged]=\"internalEventsChanged\"\n [config]=\"config\"\n (eventClicked)=\"onEventClick($event)\">\n </mn-upcoming-events>\n </div>\n </div>\n\n</div>\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%}\n"] }]
|
|
9351
9421
|
}], ctorParameters: () => [], propDecorators: { showButton: [{
|
|
9352
9422
|
type: Input
|
|
9353
9423
|
}], buttonTitle: [{
|