@besaitech/ng-design-system 0.0.77 → 0.0.79
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.
- package/calendar/index.d.ts +277 -0
- package/fesm2022/besaitech-ng-design-system-calendar.mjs +1184 -0
- package/fesm2022/besaitech-ng-design-system-calendar.mjs.map +1 -0
- package/fesm2022/besaitech-ng-design-system.mjs +946 -43
- package/fesm2022/besaitech-ng-design-system.mjs.map +1 -1
- package/index.d.ts +228 -26
- package/package.json +5 -1
- package/themes/base.css +53 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { SdsSegmentedOption } from '@besaitech/ng-design-system';
|
|
3
|
+
|
|
4
|
+
/** Which span of time the calendar is showing. */
|
|
5
|
+
type SdsEventCalendarView = 'month' | 'week' | '3day' | 'day';
|
|
6
|
+
/**
|
|
7
|
+
* How an event is tinted. These map onto the library's existing accent roles, so an event picks a
|
|
8
|
+
* meaning-free hue — it is NOT a severity and NOT a status. Anything clinical that needs to say
|
|
9
|
+
* "how bad" belongs in `sds-severity`, which has its own measured ramp.
|
|
10
|
+
*/
|
|
11
|
+
type SdsEventColor = 'brand' | 'blue' | 'green' | 'amber' | 'red' | 'indigo' | 'neutral';
|
|
12
|
+
/**
|
|
13
|
+
* One thing on the calendar.
|
|
14
|
+
*
|
|
15
|
+
* <p>`start`/`end` are Dates or anything `new Date(...)` parses. Everything is computed in the
|
|
16
|
+
* BROWSER's timezone: a calendar shows a person their own day, and converting on the way in is the
|
|
17
|
+
* caller's job if their data says otherwise.
|
|
18
|
+
*/
|
|
19
|
+
interface SdsCalendarEvent<T = unknown> {
|
|
20
|
+
id: string;
|
|
21
|
+
title: string;
|
|
22
|
+
start: Date | string | number;
|
|
23
|
+
/** Defaults to `start` + `defaultMinutes` when absent — a zero-length block is unclickable. */
|
|
24
|
+
end?: Date | string | number;
|
|
25
|
+
/** Second line, shown where there is room: a room, a reason, a phone number. */
|
|
26
|
+
subtitle?: string;
|
|
27
|
+
allDay?: boolean;
|
|
28
|
+
color?: SdsEventColor;
|
|
29
|
+
/** Struck through and faded — a cancelled appointment is still part of the day's record. */
|
|
30
|
+
cancelled?: boolean;
|
|
31
|
+
/** Whatever the caller wants back on click. The component never reads it. */
|
|
32
|
+
meta?: T;
|
|
33
|
+
}
|
|
34
|
+
/** An event resolved to real Dates, with its place in the grid worked out. */
|
|
35
|
+
interface PlacedEvent<T = unknown> {
|
|
36
|
+
event: SdsCalendarEvent<T>;
|
|
37
|
+
start: Date;
|
|
38
|
+
end: Date;
|
|
39
|
+
/** 0..1 of the visible day height. */
|
|
40
|
+
top: number;
|
|
41
|
+
height: number;
|
|
42
|
+
/** 0..1 across the column: side-by-side layout for events sharing a time. */
|
|
43
|
+
left: number;
|
|
44
|
+
width: number;
|
|
45
|
+
}
|
|
46
|
+
declare function toDate(value: Date | string | number): Date;
|
|
47
|
+
/** Minutes since local midnight. Read off the clock, never from an epoch difference — see below. */
|
|
48
|
+
declare function minutesOfDay(d: Date): number;
|
|
49
|
+
declare function startOfWeek(d: Date, firstDayOfWeek: number): Date;
|
|
50
|
+
/**
|
|
51
|
+
* The days a view shows.
|
|
52
|
+
*
|
|
53
|
+
* <p>`month` is always a 6×7 block, never 4–6 ragged rows: a grid that changes height as you page
|
|
54
|
+
* through the year makes everything below it jump.
|
|
55
|
+
*/
|
|
56
|
+
declare function visibleDays(view: SdsEventCalendarView, date: Date, firstDayOfWeek: number): Date[];
|
|
57
|
+
/** The half-open range a view covers — what a host should fetch for. */
|
|
58
|
+
declare function visibleRange(view: SdsEventCalendarView, date: Date, firstDayOfWeek: number): {
|
|
59
|
+
start: Date;
|
|
60
|
+
end: Date;
|
|
61
|
+
};
|
|
62
|
+
/** Paging: one month, one week, three days or one day at a time, in the view's own unit. */
|
|
63
|
+
declare function step(view: SdsEventCalendarView, date: Date, direction: 1 | -1): Date;
|
|
64
|
+
/** Whether an event touches a given day at all. Half-open: an event ending at 00:00 is not there. */
|
|
65
|
+
declare function occursOn(start: Date, end: Date, day: Date): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Place one day's timed events in the grid, side by side where they overlap.
|
|
68
|
+
*
|
|
69
|
+
* <p>The classic column packing: events are clustered into runs that transitively overlap, each
|
|
70
|
+
* takes the leftmost column that is free at its time, and the whole cluster is then divided by the
|
|
71
|
+
* widest point. So two appointments at 09:00 each take half the width and a third takes a third —
|
|
72
|
+
* and a 14:00 appointment with nothing beside it is full width, because it is in its own cluster.
|
|
73
|
+
*
|
|
74
|
+
* <p><b>Vertical position is read off the clock, not from an epoch difference.</b> On the two days a
|
|
75
|
+
* year when the clocks move, a 09:00–10:00 appointment is still drawn at 09:00 and one hour tall;
|
|
76
|
+
* computing it from elapsed milliseconds would slide every event after the change by an hour.
|
|
77
|
+
*
|
|
78
|
+
* <p>Events are clipped to the visible window rather than dropped: a night shift starting at 06:00
|
|
79
|
+
* on a grid that opens at 08:00 should still show, flush to the top, not vanish.
|
|
80
|
+
*/
|
|
81
|
+
declare function placeDay<T>(events: readonly SdsCalendarEvent<T>[], day: Date, startHour: number, endHour: number, defaultMinutes: number): PlacedEvent<T>[];
|
|
82
|
+
/**
|
|
83
|
+
* A dragged minute, snapped to the grid and held inside the visible window.
|
|
84
|
+
*
|
|
85
|
+
* <p>Clamping is not cosmetic: without it a drag that leaves the top of the grid proposes a time
|
|
86
|
+
* before the practice opens, and the host either books it or has to invent the same rule itself.
|
|
87
|
+
*/
|
|
88
|
+
declare function snapMinute(minute: number, stepMinutes: number, startHour: number, endHour: number): number;
|
|
89
|
+
/** The all-day (and multi-day) events touching a given day, in a stable order. */
|
|
90
|
+
declare function allDayOn<T>(events: readonly SdsCalendarEvent<T>[], day: Date, defaultMinutes: number): SdsCalendarEvent<T>[];
|
|
91
|
+
/** Every event touching a day, all-day first then by start — what a month cell lists. */
|
|
92
|
+
declare function eventsOn<T>(events: readonly SdsCalendarEvent<T>[], day: Date, defaultMinutes: number): SdsCalendarEvent<T>[];
|
|
93
|
+
|
|
94
|
+
/** Where an event was dropped. The component reports; the host decides whether it sticks. */
|
|
95
|
+
interface SdsCalendarDrop<T = unknown> {
|
|
96
|
+
event: SdsCalendarEvent<T>;
|
|
97
|
+
start: Date;
|
|
98
|
+
end: Date;
|
|
99
|
+
}
|
|
100
|
+
/** A click on empty time: what a host turns into "new appointment". */
|
|
101
|
+
interface SdsCalendarSlot {
|
|
102
|
+
start: Date;
|
|
103
|
+
end: Date;
|
|
104
|
+
}
|
|
105
|
+
interface SdsCalendarRange {
|
|
106
|
+
start: Date;
|
|
107
|
+
end: Date;
|
|
108
|
+
view: SdsEventCalendarView;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* A calendar of events — month, week, three days or one day.
|
|
112
|
+
*
|
|
113
|
+
* <p><b>Not {@link SdsCalendar}</b>, which is the month grid inside the date picker and exists to
|
|
114
|
+
* pick a date. This one shows what is ON the days, and it owns no data: the host passes `events`
|
|
115
|
+
* and listens to `rangeChange` to fetch the next lot.
|
|
116
|
+
*
|
|
117
|
+
* <p><b>It stores nothing and decides nothing.</b> `view` and `date` are two-way models, so the
|
|
118
|
+
* host can deep-link a view or restore one; every interaction is an output. That is what lets the
|
|
119
|
+
* same component serve a doctor's diary, a room's bookings or a delivery schedule without knowing
|
|
120
|
+
* what any of them are.
|
|
121
|
+
*
|
|
122
|
+
* <p><b>Dates come from `Intl`, control words come from you.</b> Day and month names follow
|
|
123
|
+
* `[locale]` automatically, so the part that is genuinely per-language needs no wiring — but
|
|
124
|
+
* "Today", "All day" and "+2 more" are inputs with English defaults, and a non-English app MUST
|
|
125
|
+
* pass them. An icon-only control's accessible name is the only name it has.
|
|
126
|
+
*
|
|
127
|
+
* <p><b>Mobile.</b> The week view keeps a readable minimum column width and scrolls sideways under
|
|
128
|
+
* its own header rather than squeezing seven columns onto a phone; the month view drops from chips
|
|
129
|
+
* to dots below `sm`. Both are CSS, so there is no resize listener to get wrong and no layout that
|
|
130
|
+
* depends on JavaScript having run.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* <sds-event-calendar
|
|
134
|
+
* [events]="appointments()"
|
|
135
|
+
* [(view)]="view"
|
|
136
|
+
* [(date)]="focused"
|
|
137
|
+
* [todayLabel]="'calendar.today' | translate"
|
|
138
|
+
* (eventClick)="open($event)"
|
|
139
|
+
* (rangeChange)="load($event)" />
|
|
140
|
+
*/
|
|
141
|
+
declare class SdsEventCalendar<T = unknown> {
|
|
142
|
+
private readonly destroyRef;
|
|
143
|
+
private readonly host;
|
|
144
|
+
readonly events: _angular_core.InputSignal<readonly SdsCalendarEvent<T>[]>;
|
|
145
|
+
/** Two-way: the host can deep-link or restore a view. */
|
|
146
|
+
readonly view: _angular_core.ModelSignal<SdsEventCalendarView>;
|
|
147
|
+
/** Two-way: any day inside the span being shown. */
|
|
148
|
+
readonly date: _angular_core.ModelSignal<Date>;
|
|
149
|
+
/** 1 = Monday (the European week), 0 = Sunday. */
|
|
150
|
+
readonly firstDayOfWeek: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
151
|
+
/** The hours the time grid shows. Events outside are clipped to the edge, never dropped. */
|
|
152
|
+
readonly startHour: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
153
|
+
readonly endHour: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
154
|
+
readonly hourHeight: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
155
|
+
/** How long an event with no `end` is assumed to last. */
|
|
156
|
+
readonly defaultMinutes: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
157
|
+
/** CSS height of the scrolling body (month: of the whole grid). */
|
|
158
|
+
readonly height: _angular_core.InputSignal<string>;
|
|
159
|
+
/** BCP-47 tag for day and month names. Empty follows the browser. */
|
|
160
|
+
readonly locale: _angular_core.InputSignal<string>;
|
|
161
|
+
readonly nowIndicator: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
162
|
+
readonly showToolbar: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
163
|
+
readonly showViewSwitcher: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
164
|
+
/** Chips per month cell before it collapses to "+N". */
|
|
165
|
+
readonly maxPerCell: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
166
|
+
/** Control words. English defaults — a non-English app must pass every one of these. */
|
|
167
|
+
readonly todayLabel: _angular_core.InputSignal<string>;
|
|
168
|
+
readonly previousLabel: _angular_core.InputSignal<string>;
|
|
169
|
+
readonly nextLabel: _angular_core.InputSignal<string>;
|
|
170
|
+
readonly allDayLabel: _angular_core.InputSignal<string>;
|
|
171
|
+
/** `{count}` is substituted. */
|
|
172
|
+
readonly moreLabel: _angular_core.InputSignal<string>;
|
|
173
|
+
readonly viewSwitcherLabel: _angular_core.InputSignal<string>;
|
|
174
|
+
readonly monthLabel: _angular_core.InputSignal<string>;
|
|
175
|
+
readonly weekLabel: _angular_core.InputSignal<string>;
|
|
176
|
+
readonly threeDayLabel: _angular_core.InputSignal<string>;
|
|
177
|
+
readonly dayLabel: _angular_core.InputSignal<string>;
|
|
178
|
+
/**
|
|
179
|
+
* Let an event be dragged to another time or day. <b>Opt-in</b>: a calendar whose host does not
|
|
180
|
+
* persist the move would show an appointment settle somewhere it has not been booked, which is
|
|
181
|
+
* worse than not offering the gesture.
|
|
182
|
+
*/
|
|
183
|
+
readonly draggable: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
184
|
+
/** Drag across empty time to propose a span. The plain click keeps emitting `slotClick`. */
|
|
185
|
+
readonly selectable: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
186
|
+
/** Both gestures snap to this. 15 minutes is the finest grid anybody reads off a week view. */
|
|
187
|
+
readonly snapMinutes: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
188
|
+
readonly eventClick: _angular_core.OutputEmitterRef<SdsCalendarEvent<T>>;
|
|
189
|
+
/**
|
|
190
|
+
* An event was dragged somewhere else. The component does NOT move it: it reports where it was
|
|
191
|
+
* dropped and redraws from `events` as before, so the diary only changes once the host says it
|
|
192
|
+
* did. A drag that moved the chip locally would show a booking that does not exist whenever the
|
|
193
|
+
* write is refused.
|
|
194
|
+
*/
|
|
195
|
+
readonly eventDrop: _angular_core.OutputEmitterRef<SdsCalendarDrop<T>>;
|
|
196
|
+
/** A span was dragged out across empty time — "book something from here to here". */
|
|
197
|
+
readonly rangeSelect: _angular_core.OutputEmitterRef<SdsCalendarSlot>;
|
|
198
|
+
/** A click on empty time, snapped to the hour that was clicked. */
|
|
199
|
+
readonly slotClick: _angular_core.OutputEmitterRef<SdsCalendarSlot>;
|
|
200
|
+
readonly dayClick: _angular_core.OutputEmitterRef<Date>;
|
|
201
|
+
/** The span now on screen — fetch for this. Fires whenever the view or the date moves. */
|
|
202
|
+
readonly rangeChange: _angular_core.OutputEmitterRef<SdsCalendarRange>;
|
|
203
|
+
/** Ticks the now-indicator. One timer, cleared with the component. */
|
|
204
|
+
private readonly now;
|
|
205
|
+
/** The gesture in flight, if any. Null between gestures — there is never more than one. */
|
|
206
|
+
private readonly drag;
|
|
207
|
+
/** Set once the pointer has moved far enough to be a drag rather than a click. */
|
|
208
|
+
private readonly dragging;
|
|
209
|
+
/**
|
|
210
|
+
* A finished drag still produces a click, and it arrives AFTER pointerup — so checking
|
|
211
|
+
* "am I dragging" inside the click handler is always false by then. Measured: dropping an event
|
|
212
|
+
* emitted `eventDrop` AND `slotClick`, so a host both moved the appointment and opened a
|
|
213
|
+
* "new appointment" panel for where the finger lifted. This survives until the click it has to
|
|
214
|
+
* swallow, and is cleared either way.
|
|
215
|
+
*/
|
|
216
|
+
private suppressClick;
|
|
217
|
+
constructor();
|
|
218
|
+
protected readonly days: _angular_core.Signal<Date[]>;
|
|
219
|
+
protected readonly hours: _angular_core.Signal<number[]>;
|
|
220
|
+
protected readonly bodyHeight: _angular_core.Signal<number>;
|
|
221
|
+
protected readonly columns: _angular_core.Signal<string>;
|
|
222
|
+
/** Keeps a day column readable on a phone; the grid scrolls sideways instead of squeezing. */
|
|
223
|
+
protected readonly minWidth: _angular_core.Signal<number>;
|
|
224
|
+
private readonly intl;
|
|
225
|
+
protected readonly weekdayNames: _angular_core.Signal<string[]>;
|
|
226
|
+
protected readonly title: _angular_core.Signal<string>;
|
|
227
|
+
protected readonly viewOptions: _angular_core.Signal<SdsSegmentedOption<unknown>[]>;
|
|
228
|
+
protected readonly hasAllDay: _angular_core.Signal<boolean>;
|
|
229
|
+
protected readonly nowTop: _angular_core.Signal<number | null>;
|
|
230
|
+
protected placed(day: Date): PlacedEvent<T>[];
|
|
231
|
+
protected allDayEvents(day: Date): SdsCalendarEvent<T>[];
|
|
232
|
+
protected cellEvents(day: Date): SdsCalendarEvent<T>[];
|
|
233
|
+
protected isToday(day: Date): boolean;
|
|
234
|
+
protected inFocusedMonth(day: Date): boolean;
|
|
235
|
+
protected shortWeekday(day: Date): string;
|
|
236
|
+
protected hourLabel(hour: number): string;
|
|
237
|
+
protected clock(value: Date | string | number): string;
|
|
238
|
+
protected hourTop(hour: number): number;
|
|
239
|
+
protected chipClass(e: SdsCalendarEvent<T>): string;
|
|
240
|
+
protected dotClass(e: SdsCalendarEvent<T>): string;
|
|
241
|
+
protected setView(next: unknown): void;
|
|
242
|
+
protected move(direction: 1 | -1): void;
|
|
243
|
+
protected goToday(): void;
|
|
244
|
+
/** A click on empty grid becomes a slot of `defaultMinutes`, snapped to the hour clicked. */
|
|
245
|
+
protected onGridClick(event: MouseEvent, day: Date): void;
|
|
246
|
+
protected canDrag(placed: PlacedEvent<T>): boolean;
|
|
247
|
+
protected isDragging(placed: PlacedEvent<T>): boolean;
|
|
248
|
+
/** The dashed block: where this drag would land on this column, or null. */
|
|
249
|
+
protected ghostFor(dayIndex: number): {
|
|
250
|
+
top: number;
|
|
251
|
+
height: number;
|
|
252
|
+
label: string;
|
|
253
|
+
} | null;
|
|
254
|
+
protected startSelect(event: PointerEvent, dayIndex: number): void;
|
|
255
|
+
protected startMove(event: PointerEvent, placed: PlacedEvent<T>, dayIndex: number): void;
|
|
256
|
+
/**
|
|
257
|
+
* Listeners are attached for the gesture and removed on release, rather than living on the host
|
|
258
|
+
* for the component's whole life. A calendar sitting idle on a dashboard should not be handling
|
|
259
|
+
* every pointer move on the page.
|
|
260
|
+
*/
|
|
261
|
+
private begin;
|
|
262
|
+
private finish;
|
|
263
|
+
private snap;
|
|
264
|
+
private minuteAt;
|
|
265
|
+
private atMinute;
|
|
266
|
+
private columnAt;
|
|
267
|
+
private columnFor;
|
|
268
|
+
/** True once, for the click that a just-finished drag is about to produce. */
|
|
269
|
+
private swallowed;
|
|
270
|
+
private hhmm;
|
|
271
|
+
protected onEventClick(event: MouseEvent, placed: PlacedEvent<T>): void;
|
|
272
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdsEventCalendar<any>, never>;
|
|
273
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SdsEventCalendar<any>, "sds-event-calendar", never, { "events": { "alias": "events"; "required": false; "isSignal": true; }; "view": { "alias": "view"; "required": false; "isSignal": true; }; "date": { "alias": "date"; "required": false; "isSignal": true; }; "firstDayOfWeek": { "alias": "firstDayOfWeek"; "required": false; "isSignal": true; }; "startHour": { "alias": "startHour"; "required": false; "isSignal": true; }; "endHour": { "alias": "endHour"; "required": false; "isSignal": true; }; "hourHeight": { "alias": "hourHeight"; "required": false; "isSignal": true; }; "defaultMinutes": { "alias": "defaultMinutes"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "nowIndicator": { "alias": "nowIndicator"; "required": false; "isSignal": true; }; "showToolbar": { "alias": "showToolbar"; "required": false; "isSignal": true; }; "showViewSwitcher": { "alias": "showViewSwitcher"; "required": false; "isSignal": true; }; "maxPerCell": { "alias": "maxPerCell"; "required": false; "isSignal": true; }; "todayLabel": { "alias": "todayLabel"; "required": false; "isSignal": true; }; "previousLabel": { "alias": "previousLabel"; "required": false; "isSignal": true; }; "nextLabel": { "alias": "nextLabel"; "required": false; "isSignal": true; }; "allDayLabel": { "alias": "allDayLabel"; "required": false; "isSignal": true; }; "moreLabel": { "alias": "moreLabel"; "required": false; "isSignal": true; }; "viewSwitcherLabel": { "alias": "viewSwitcherLabel"; "required": false; "isSignal": true; }; "monthLabel": { "alias": "monthLabel"; "required": false; "isSignal": true; }; "weekLabel": { "alias": "weekLabel"; "required": false; "isSignal": true; }; "threeDayLabel": { "alias": "threeDayLabel"; "required": false; "isSignal": true; }; "dayLabel": { "alias": "dayLabel"; "required": false; "isSignal": true; }; "draggable": { "alias": "draggable"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "snapMinutes": { "alias": "snapMinutes"; "required": false; "isSignal": true; }; }, { "view": "viewChange"; "date": "dateChange"; "eventClick": "eventClick"; "eventDrop": "eventDrop"; "rangeSelect": "rangeSelect"; "slotClick": "slotClick"; "dayClick": "dayClick"; "rangeChange": "rangeChange"; }, never, never, true, never>;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export { SdsEventCalendar, allDayOn, eventsOn, minutesOfDay, occursOn, placeDay, snapMinute, startOfWeek, step, toDate, visibleDays, visibleRange };
|
|
277
|
+
export type { PlacedEvent, SdsCalendarDrop, SdsCalendarEvent, SdsCalendarRange, SdsCalendarSlot, SdsEventCalendarView, SdsEventColor };
|