ud-components 0.5.21 → 0.5.23

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.
@@ -1,5 +1,5 @@
1
1
  import { OnDestroy, TemplateRef } from '@angular/core';
2
- import { CalendarMode, CalendarSlot, CalendarUser, CalendarView } from './calendar.interface';
2
+ import { CalendarMode, CalendarSlot, CalendarUser, CalendarView, RescheduleContext } from './calendar.interface';
3
3
  import * as i0 from "@angular/core";
4
4
  export declare class CalendarComponent implements OnDestroy {
5
5
  slots: import("@angular/core").InputSignal<CalendarSlot[]>;
@@ -20,10 +20,44 @@ export declare class CalendarComponent implements OnDestroy {
20
20
  * free-text input.
21
21
  */
22
22
  bookableUsers: import("@angular/core").InputSignal<CalendarUser[]>;
23
+ /**
24
+ * Optional cutoff: any cell whose start is strictly before this Date is
25
+ * visually disabled (grey, no hover, no cursor) and click-blocked. The
26
+ * "Add slot" toolbar button pre-fills the modal with this timestamp
27
+ * (rounded up to the next `slotDuration` increment) instead of `new Date()`.
28
+ * The modal's save handler also refuses to emit if a hand-edited time
29
+ * lands before the cutoff. Applies to both admin and student modes.
30
+ */
31
+ minSlotStart: import("@angular/core").InputSignal<Date | null>;
32
+ /**
33
+ * Optional initial calendar position. Overrides the internal `navDate`
34
+ * which otherwise defaults to "today / now". Useful for reschedule flows
35
+ * or deep links that should land on a specific day.
36
+ */
37
+ defaultDate: import("@angular/core").InputSignal<Date | null>;
38
+ /**
39
+ * When set, the "Add slot" modal flips into reschedule mode:
40
+ * • title becomes "Move appointment"
41
+ * • a lede shows the original time being moved away from
42
+ * • the user picker auto-selects the lone bookable user (or `userId` if
43
+ * provided) so the admin doesn't have to choose from a list of one
44
+ * • Booked defaults to true (a reschedule keeps a booking, not a fresh slot)
45
+ * • the confirm button reads "Reschedule" instead of "Save"
46
+ *
47
+ * The wire-level event is still `slotAdded` — consumers route it to their
48
+ * reschedule API based on their own context (no extra output needed).
49
+ */
50
+ rescheduleContext: import("@angular/core").InputSignal<RescheduleContext | null>;
23
51
  slotAdded: import("@angular/core").OutputEmitterRef<CalendarSlot>;
24
52
  slotUpdated: import("@angular/core").OutputEmitterRef<CalendarSlot>;
25
53
  slotRemoved: import("@angular/core").OutputEmitterRef<string>;
26
54
  slotBooked: import("@angular/core").OutputEmitterRef<CalendarSlot>;
55
+ /** Fired when a user attempts to interact with a cell before `minSlotStart`. */
56
+ slotBlocked: import("@angular/core").OutputEmitterRef<{
57
+ start: Date;
58
+ end: Date;
59
+ reason: "before-min";
60
+ }>;
27
61
  activeView: import("@angular/core").WritableSignal<CalendarView>;
28
62
  navDate: import("@angular/core").WritableSignal<Date>;
29
63
  readonly viewOptions: {
@@ -87,11 +121,26 @@ export declare class CalendarComponent implements OnDestroy {
87
121
  slotTextColor(slot: CalendarSlot): string;
88
122
  slotBorderColor(slot: CalendarSlot): string;
89
123
  formatSlotTime(slot: CalendarSlot): string;
124
+ /**
125
+ * Pretty range like "Mon Jun 8 · 2:00 – 2:30 PM". Used by reschedule-mode's
126
+ * "Moving from …" lede so the admin sees where the appointment is coming
127
+ * from while picking the new time.
128
+ */
129
+ private formatMomentRange;
90
130
  isToday(day: Date): boolean;
91
131
  isCurrentMonth(day: Date): boolean;
92
132
  dayLabel(day: Date): string;
93
133
  dayNum(day: Date): number;
94
134
  onCellClick(day: Date, hour: number, minute: number): void;
135
+ /**
136
+ * True when the cell at (day, hour, minute) starts before `minSlotStart`.
137
+ * Templates bind a CSS class on this to grey out past cells; the click
138
+ * handler also bails so consumers don't see invalid `slotAdded` events.
139
+ */
140
+ isCellDisabled(day: Date, hour: number, minute: number): boolean;
141
+ /** True when `day` is fully before `minSlotStart` (used by month view). */
142
+ isDayDisabled(day: Date): boolean;
143
+ private combineDayTime;
95
144
  onSlotMouseDown(e: MouseEvent, slot: CalendarSlot): void;
96
145
  onDocMouseMove(e: MouseEvent): void;
97
146
  onDocMouseUp(e: MouseEvent): void;
@@ -102,6 +151,8 @@ export declare class CalendarComponent implements OnDestroy {
102
151
  previewHeight(): number;
103
152
  private getGridTarget;
104
153
  openAddModal(day?: Date, hour?: number, minute?: number): void;
154
+ /** Round a Date up to the next `slotDuration` increment (minute-aligned). */
155
+ private roundUpToSlot;
105
156
  openEditModal(slot: CalendarSlot): void;
106
157
  private get slotFormErrors();
107
158
  private slotTimeValidator;
@@ -116,5 +167,5 @@ export declare class CalendarComponent implements OnDestroy {
116
167
  private formatTime;
117
168
  private hexToRgba;
118
169
  static ɵfac: i0.ɵɵFactoryDeclaration<CalendarComponent, never>;
119
- static ɵcmp: i0.ɵɵComponentDeclaration<CalendarComponent, "ud-calendar", never, { "slots": { "alias": "slots"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "defaultView": { "alias": "defaultView"; "required": false; "isSignal": true; }; "slotDuration": { "alias": "slotDuration"; "required": false; "isSignal": true; }; "minHour": { "alias": "minHour"; "required": false; "isSignal": true; }; "maxHour": { "alias": "maxHour"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "bookableUsers": { "alias": "bookableUsers"; "required": false; "isSignal": true; }; }, { "slotAdded": "slotAdded"; "slotUpdated": "slotUpdated"; "slotRemoved": "slotRemoved"; "slotBooked": "slotBooked"; }, never, never, true, never>;
170
+ static ɵcmp: i0.ɵɵComponentDeclaration<CalendarComponent, "ud-calendar", never, { "slots": { "alias": "slots"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "defaultView": { "alias": "defaultView"; "required": false; "isSignal": true; }; "slotDuration": { "alias": "slotDuration"; "required": false; "isSignal": true; }; "minHour": { "alias": "minHour"; "required": false; "isSignal": true; }; "maxHour": { "alias": "maxHour"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "bookableUsers": { "alias": "bookableUsers"; "required": false; "isSignal": true; }; "minSlotStart": { "alias": "minSlotStart"; "required": false; "isSignal": true; }; "defaultDate": { "alias": "defaultDate"; "required": false; "isSignal": true; }; "rescheduleContext": { "alias": "rescheduleContext"; "required": false; "isSignal": true; }; }, { "slotAdded": "slotAdded"; "slotUpdated": "slotUpdated"; "slotRemoved": "slotRemoved"; "slotBooked": "slotBooked"; "slotBlocked": "slotBlocked"; }, never, never, true, never>;
120
171
  }
@@ -19,6 +19,28 @@ export interface CalendarUser {
19
19
  }
20
20
  export type CalendarView = 'week' | 'month' | 'day';
21
21
  export type CalendarMode = 'admin' | 'student' | 'readonly';
22
+ /**
23
+ * Context the host supplies when the calendar should be used to *move* an
24
+ * existing booking instead of creating a new one. When set on
25
+ * `<ud-calendar [rescheduleContext]>`, the add-slot modal flips into
26
+ * "move appointment" mode (title, lede, save label, defaults).
27
+ *
28
+ * The component does not call any reschedule API itself — it still emits
29
+ * `slotAdded` with the picked slot and the host decides whether to POST a
30
+ * new booking or PUT a reschedule.
31
+ */
32
+ export interface RescheduleContext {
33
+ /** The current appointment's start, used in the modal's "Moving from …" lede. */
34
+ originalStart: Date;
35
+ /** The current appointment's end — paired with `originalStart` for the label. */
36
+ originalEnd: Date;
37
+ /**
38
+ * Optional id of the user the appointment belongs to. When provided, the
39
+ * add-slot modal pre-selects this user in the `bookableUsers` picker so the
40
+ * admin doesn't have to choose from a list of one.
41
+ */
42
+ userId?: string;
43
+ }
22
44
  /**
23
45
  * Curated slot colours. Assign one to `CalendarSlot.color` for a consistent,
24
46
  * on-brand palette instead of hand-picking hex values — the calendar tints these
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ud-components",
3
- "version": "0.5.21",
3
+ "version": "0.5.23",
4
4
  "peerDependencies": {
5
5
  "@angular/cdk": ">=19",
6
6
  "@angular/common": ">=19",
@@ -21,30 +21,6 @@
21
21
  ".": {
22
22
  "types": "./index.d.ts",
23
23
  "default": "./fesm2022/ud-components.mjs"
24
- },
25
- "./enums/role.enum": {
26
- "types": "./enums/role.enum.d.ts",
27
- "default": "./enums/role.enum.js"
28
- },
29
- "./lib/kpi/kpi.enum": {
30
- "types": "./lib/kpi/kpi.enum.d.ts",
31
- "default": "./lib/kpi/kpi.enum.js"
32
- },
33
- "./lib/edit-view/edit-view-section.directive": {
34
- "types": "./lib/edit-view/edit-view-section.directive.d.ts",
35
- "default": "./lib/edit-view/edit-view-section.directive.js"
36
- },
37
- "./interfaces/page-request.interface": {
38
- "types": "./interfaces/page-request.interface.d.ts",
39
- "default": "./interfaces/page-request.interface.js"
40
- },
41
- "./interfaces/table.interface": {
42
- "types": "./interfaces/table.interface.d.ts",
43
- "default": "./interfaces/table.interface.js"
44
- },
45
- "./interfaces/table-display-column.interface": {
46
- "types": "./interfaces/table-display-column.interface.d.ts",
47
- "default": "./interfaces/table-display-column.interface.js"
48
24
  }
49
25
  }
50
- }
26
+ }