mn-angular-lib 1.0.119 → 1.0.121

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.
@@ -229,6 +229,9 @@ const mnButtonVariants = tv({
229
229
  outline: 'bg-transparent border',
230
230
  text: 'bg-transparent',
231
231
  textUnderline: 'bg-transparent underline underline-offset-2',
232
+ // Transparent button that only reveals a subtle surface on hover — the idiomatic
233
+ // icon-button look. Colors resolved via compoundVariants.
234
+ ghost: 'bg-transparent',
232
235
  },
233
236
  // Intentionally empty; resolved via compoundVariants
234
237
  color: {
@@ -251,6 +254,14 @@ const mnButtonVariants = tv({
251
254
  three_xl: 'rounded-3xl',
252
255
  four_xl: 'rounded-4xl',
253
256
  },
257
+ // Icon-button shape. `circle`/`square` drop the text padding and make the button a
258
+ // fixed square sized off `size` (see compoundVariants), overriding `borderRadius`.
259
+ // `default` leaves a normal text button untouched.
260
+ shape: {
261
+ default: '',
262
+ circle: 'p-0 shrink-0 rounded-full',
263
+ square: 'p-0 shrink-0 rounded-md',
264
+ },
254
265
  disabled: {
255
266
  true: 'opacity-50 pointer-events-none',
256
267
  },
@@ -332,12 +343,36 @@ const mnButtonVariants = tv({
332
343
  { variant: 'textUnderline', color: 'success', hover: true, class: 'hover:bg-success/10' },
333
344
  { variant: 'textUnderline', color: 'accent', hover: true, class: 'hover:bg-accent/10' },
334
345
  { variant: 'textUnderline', color: 'gray', hover: true, class: 'hover:bg-base-content/10' },
346
+ // Ghost — base (transparent, colored text/icon)
347
+ { variant: 'ghost', color: 'primary', class: 'text-primary' },
348
+ { variant: 'ghost', color: 'secondary', class: 'text-neutral' },
349
+ { variant: 'ghost', color: 'danger', class: 'text-error' },
350
+ { variant: 'ghost', color: 'warning', class: 'text-warning' },
351
+ { variant: 'ghost', color: 'success', class: 'text-success' },
352
+ { variant: 'ghost', color: 'accent', class: 'text-accent' },
353
+ { variant: 'ghost', color: 'gray', class: 'text-base-content' },
354
+ // Ghost — hover (reveal a subtle surface in the button's own hue)
355
+ { variant: 'ghost', color: 'primary', hover: true, class: 'hover:bg-primary/10' },
356
+ { variant: 'ghost', color: 'secondary', hover: true, class: 'hover:bg-neutral/10' },
357
+ { variant: 'ghost', color: 'danger', hover: true, class: 'hover:bg-error/10' },
358
+ { variant: 'ghost', color: 'warning', hover: true, class: 'hover:bg-warning/10' },
359
+ { variant: 'ghost', color: 'success', hover: true, class: 'hover:bg-success/10' },
360
+ { variant: 'ghost', color: 'accent', hover: true, class: 'hover:bg-accent/10' },
361
+ { variant: 'ghost', color: 'gray', hover: true, class: 'hover:bg-base-content/10' },
362
+ // Icon-button square sizing — applies to both circle and square shapes, sized off `size`.
363
+ { shape: 'circle', size: 'sm', class: 'h-8 w-8' },
364
+ { shape: 'circle', size: 'md', class: 'h-10 w-10' },
365
+ { shape: 'circle', size: 'lg', class: 'h-12 w-12' },
366
+ { shape: 'square', size: 'sm', class: 'h-8 w-8' },
367
+ { shape: 'square', size: 'md', class: 'h-10 w-10' },
368
+ { shape: 'square', size: 'lg', class: 'h-12 w-12' },
335
369
  ],
336
370
  defaultVariants: {
337
371
  size: 'md',
338
372
  variant: 'fill',
339
373
  color: 'primary',
340
374
  borderRadius: 'lg',
375
+ shape: 'default',
341
376
  disabled: false,
342
377
  loading: false,
343
378
  wrap: false,
@@ -355,6 +390,7 @@ class MnButton {
355
390
  variant: this.data.variant,
356
391
  color: this.data.color,
357
392
  borderRadius: this.data.borderRadius,
393
+ shape: this.data.shape,
358
394
  disabled: this.data.disabled,
359
395
  loading: this.data.loading,
360
396
  wrap: this.data.wrap,
@@ -9210,7 +9246,6 @@ class CalendarViewComponent {
9210
9246
  focusDay = new Date();
9211
9247
  viewOptions = [];
9212
9248
  isMobileView = false;
9213
- isTabletView = false;
9214
9249
  /** BehaviorSubject so late-subscribing child views receive the last emitted events. */
9215
9250
  internalEventsChanged = new BehaviorSubject([]);
9216
9251
  /** Subject for broadcasting focus-day changes to child views. */
@@ -9367,7 +9402,6 @@ class CalendarViewComponent {
9367
9402
  // view takes over earlier — and consumers can tune where via `mobileBreakpoint`.
9368
9403
  const breakpoint = this.config.mobileBreakpoint;
9369
9404
  this.isMobileView = width < breakpoint;
9370
- this.isTabletView = width >= breakpoint && width < 1024;
9371
9405
  if (this.isMobileView && !wasMobile) {
9372
9406
  this.currentView = CalendarView.DAY;
9373
9407
  }
@@ -9400,7 +9434,7 @@ class CalendarViewComponent {
9400
9434
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9401
9435
  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: [
9402
9436
  provideMnCalendarConfig(DEFAULT_CALENDAR_CONFIG),
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]" }] });
9437
+ ], ngImport: i0, template: "<div class=\"w-full h-full flex flex-col\" role=\"application\" aria-label=\"Calendar\">\n\n <!-- Toolbar has one layout below the desktop (`lg`) width and one at it. Below\n `lg`: a controls row (Today anchored left, the right-hand cluster anchored\n right) with the \u2039 date \u203A nav centred on its own row beneath \u2014 the same\n shape from phone through tablet, so nothing wraps into a lopsided diagonal.\n At `lg`, where the upcoming-events sidebar also appears, the nav folds up\n inline after Today and the whole toolbar becomes a single row. The one JS\n breakpoint (`mobileBreakpoint`) governs the phone controls \u2014 icon-only\n picker, hidden view-switcher, forced day view \u2014 not the toolbar's shape. -->\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=\"shrink-0\"\n mnButton\n type=\"button\">\n {{ config.todayLabel }}\n </button>\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. Below `lg` it\n sits centred on its own row (basis-full, ordered last) \u2014 a period header\n above the grid, tight rather than flinging the arrows to the row's edges.\n At `lg` it folds inline right after Today, taking a stable min-width so\n the arrows don't jitter as the label's length changes. -->\n <div class=\"flex basis-full order-last justify-center items-center gap-1\n lg:basis-auto lg:order-none 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\"\n 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 <!-- Right-hand controls travel together as one cluster with `ml-auto`, so they\n hold to the right of the row on every width \u2014 and when space runs short the\n whole group wraps to a new line as a unit rather than stranding a lone\n button. On desktop the cluster lands above the upcoming-events sidebar,\n using that otherwise-empty space. -->\n <div class=\"ml-auto flex items-center gap-2\">\n <!-- On mobile the picker collapses to its icon-only variant. -->\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=\"block\"></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=\"flex border border-base-300 rounded-md overflow-hidden\" 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). -->\n @if (buttons.length || showButton) {\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 }\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]" }] });
9404
9438
  }
9405
9439
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CalendarViewComponent, decorators: [{
9406
9440
  type: Component,
@@ -9417,7 +9451,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
9417
9451
  LucideChevronRight
9418
9452
  ], providers: [
9419
9453
  provideMnCalendarConfig(DEFAULT_CALENDAR_CONFIG),
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"] }]
9454
+ ], template: "<div class=\"w-full h-full flex flex-col\" role=\"application\" aria-label=\"Calendar\">\n\n <!-- Toolbar has one layout below the desktop (`lg`) width and one at it. Below\n `lg`: a controls row (Today anchored left, the right-hand cluster anchored\n right) with the \u2039 date \u203A nav centred on its own row beneath \u2014 the same\n shape from phone through tablet, so nothing wraps into a lopsided diagonal.\n At `lg`, where the upcoming-events sidebar also appears, the nav folds up\n inline after Today and the whole toolbar becomes a single row. The one JS\n breakpoint (`mobileBreakpoint`) governs the phone controls \u2014 icon-only\n picker, hidden view-switcher, forced day view \u2014 not the toolbar's shape. -->\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=\"shrink-0\"\n mnButton\n type=\"button\">\n {{ config.todayLabel }}\n </button>\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. Below `lg` it\n sits centred on its own row (basis-full, ordered last) \u2014 a period header\n above the grid, tight rather than flinging the arrows to the row's edges.\n At `lg` it folds inline right after Today, taking a stable min-width so\n the arrows don't jitter as the label's length changes. -->\n <div class=\"flex basis-full order-last justify-center items-center gap-1\n lg:basis-auto lg:order-none 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\"\n 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 <!-- Right-hand controls travel together as one cluster with `ml-auto`, so they\n hold to the right of the row on every width \u2014 and when space runs short the\n whole group wraps to a new line as a unit rather than stranding a lone\n button. On desktop the cluster lands above the upcoming-events sidebar,\n using that otherwise-empty space. -->\n <div class=\"ml-auto flex items-center gap-2\">\n <!-- On mobile the picker collapses to its icon-only variant. -->\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=\"block\"></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=\"flex border border-base-300 rounded-md overflow-hidden\" 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). -->\n @if (buttons.length || showButton) {\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 }\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"] }]
9421
9455
  }], ctorParameters: () => [], propDecorators: { showButton: [{
9422
9456
  type: Input
9423
9457
  }], buttonTitle: [{