ngx-resource-scheduler 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -81,7 +81,6 @@ events: SchedulerEvent[] = [
81
81
  | `slotLineStyle` | `slot` | `slot`, `hour`, or `both` |
82
82
  | `readonly` | `false` | Disable interactions |
83
83
  | `timezone` | `local` | `local`, `UTC`, or IANA zone (e.g. `Europe/Kiev`) |
84
- | `weekStartsOn` | `1` | First day of week. 0 = Sunday |
85
84
 
86
85
  > **Important**
87
86
  >
@@ -0,0 +1,592 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, Output, Input, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
3
+ import { fromZonedTime, toZonedTime } from 'date-fns-tz';
4
+ import * as i1 from '@angular/common';
5
+ import { CommonModule } from '@angular/common';
6
+
7
+ class NgxResourceSchedulerComponent {
8
+ constructor() {
9
+ this.resources = [];
10
+ this.events = [];
11
+ // --- FLEXIBILITY ---
12
+ this.nDays = 7; // clamp [1..7]
13
+ this.primaryAxis = 'days';
14
+ // --- TIME WINDOW (vertical) ---
15
+ this.dayStart = '08:00'; // HH:mm
16
+ this.dayEnd = '20:00'; // HH:mm
17
+ this.slotDuration = '00:30'; // HH:mm (grid resolution)
18
+ this.snapToSlot = true;
19
+ this.showSlotLines = true;
20
+ this.slotLineStyle = 'slot';
21
+ // NAVIGATION
22
+ this.showToolbar = true;
23
+ this.prevLabel = '‹';
24
+ this.nextLabel = '›';
25
+ this.lastRangeKey = null;
26
+ // i18n
27
+ this.showDaysResourcesLabel = true;
28
+ this.daysLabel = 'days';
29
+ this.resourcesLabel = 'resources';
30
+ this.todayLabel = 'Today';
31
+ this.readonly = false;
32
+ // --- OUTPUTS ---
33
+ this.slotClick = new EventEmitter();
34
+ this.eventClick = new EventEmitter();
35
+ this.eventChange = new EventEmitter();
36
+ this.rangeChange = new EventEmitter();
37
+ this.startDateChange = new EventEmitter();
38
+ // --- INTERNAL LAYOUT CONSTANTS ---
39
+ this.pxPerMinute = 1; // 60px per hour
40
+ // --- COMPUTED ---
41
+ this.visibleDays = [];
42
+ this.primaryColumns = [];
43
+ this.secondaryColumns = [];
44
+ this.dayStartMinutes = 8 * 60;
45
+ this.dayEndMinutes = 20 * 60;
46
+ this.slotMinutes = 30;
47
+ // ------------- TRACK BYS -------------
48
+ this.trackPrimary = (_, c) => c.key;
49
+ this.trackSecondary = (_, c) => c.key;
50
+ this.trackEvent = (_, e) => e.id;
51
+ }
52
+ ngOnChanges(_) {
53
+ this.recompute();
54
+ }
55
+ // ---------- PUBLIC METHODS ---------
56
+ /** Navigate by one "page" (nDays) backward */
57
+ prev() {
58
+ this.navigatePrev();
59
+ }
60
+ /** Navigate by one "page" (nDays) forward */
61
+ next() {
62
+ this.navigateNext();
63
+ }
64
+ /** Go to today (start of day) */
65
+ today() {
66
+ this.navigateToday();
67
+ }
68
+ /** Programmatically set the visible start date */
69
+ goToDate(d) {
70
+ this.setStartDate(this.startOfDay(d));
71
+ }
72
+ // ---------- TEMPLATE HELPERS ----------
73
+ get timelineHeightPx() {
74
+ return Math.max(0, (this.dayEndMinutes - this.dayStartMinutes) * this.pxPerMinute);
75
+ }
76
+ get hourLabels() {
77
+ const startH = Math.floor(this.dayStartMinutes / 60);
78
+ const endH = Math.floor(this.dayEndMinutes / 60);
79
+ const hours = [];
80
+ for (let h = startH; h <= endH; h++)
81
+ hours.push(h);
82
+ return hours;
83
+ }
84
+ get slotLines() {
85
+ if (!this.showSlotLines)
86
+ return [];
87
+ const spanMin = this.dayEndMinutes - this.dayStartMinutes;
88
+ if (spanMin <= 0 || this.slotMinutes <= 0)
89
+ return [];
90
+ const lines = [];
91
+ for (let m = 0; m <= spanMin; m += this.slotMinutes) {
92
+ const absoluteMin = this.dayStartMinutes + m;
93
+ // true at hh:30 exactly (e.g., 08:30, 09:30, ...)
94
+ const isHalfHour = absoluteMin % 60 === 30;
95
+ lines.push({
96
+ top: m * this.pxPerMinute,
97
+ isHalfHour,
98
+ });
99
+ }
100
+ return lines;
101
+ }
102
+ get hourLineOffsetsPx() {
103
+ const spanMin = this.dayEndMinutes - this.dayStartMinutes;
104
+ if (spanMin <= 0)
105
+ return [];
106
+ const offsets = [];
107
+ const firstHourMin = Math.ceil(this.dayStartMinutes / 60) * 60;
108
+ for (let m = firstHourMin; m <= this.dayEndMinutes; m += 60) {
109
+ offsets.push((m - this.dayStartMinutes) * this.pxPerMinute);
110
+ }
111
+ // Also include the top edge at 0 for a clean line
112
+ offsets.unshift(0);
113
+ return offsets;
114
+ }
115
+ get rangeTitle() {
116
+ if (!this.visibleDays.length)
117
+ return '';
118
+ const start = this.visibleDays[0];
119
+ const end = this.visibleDays[this.visibleDays.length - 1];
120
+ const sameMonth = start.getFullYear() === end.getFullYear() && start.getMonth() === end.getMonth();
121
+ const opts = { month: 'short', day: 'numeric' };
122
+ if (this.visibleDays.length === 1) {
123
+ return start.toLocaleDateString(this.locale, { weekday: 'short', ...opts });
124
+ }
125
+ if (sameMonth) {
126
+ const month = start.toLocaleDateString(this.locale, { month: 'short' });
127
+ return `${month} ${start.getDate()}–${end.getDate()}`;
128
+ }
129
+ return `${start.toLocaleDateString(this.locale, opts)} – ${end.toLocaleDateString(this.locale, opts)}`;
130
+ }
131
+ /**
132
+ * Returns events that intersect a given (day, resource) cell.
133
+ */
134
+ eventsForCell(day, resourceId) {
135
+ const { startUtc, endUtc } = this.windowBoundsUtc(day);
136
+ const list = this.events
137
+ .filter((e) => e.resourceId === resourceId &&
138
+ e.start < endUtc &&
139
+ e.end > startUtc)
140
+ .sort((a, b) => a.start.getTime() - b.start.getTime());
141
+ return this.layoutOverlaps(list);
142
+ }
143
+ /**
144
+ * CSS positioning for an event within a day cell.
145
+ */
146
+ styleForEvent(e, day) {
147
+ const { startUtc: windowStartUtc, endUtc: windowEndUtc } = this.windowBoundsUtc(day);
148
+ // If event doesn't intersect visible window, don't render it
149
+ if (e.end <= windowStartUtc || e.start >= windowEndUtc) {
150
+ return { display: 'none' };
151
+ }
152
+ const start = new Date(Math.max(e.start.getTime(), windowStartUtc.getTime()));
153
+ const end = new Date(Math.min(e.end.getTime(), windowEndUtc.getTime()));
154
+ const topMin = (start.getTime() - windowStartUtc.getTime()) / 60000;
155
+ const durMin = Math.max(10, (end.getTime() - start.getTime()) / 60000);
156
+ // overlap columns (same as you already have)
157
+ const gapPx = 6;
158
+ const cols = Math.max(1, e._cols);
159
+ const col = Math.max(0, e._col);
160
+ const widthExpr = `calc(${100 / cols}% - ${gapPx}px)`;
161
+ const leftExpr = `calc(${(100 * col) / cols}% + ${gapPx / 2}px)`;
162
+ return {
163
+ top: `${topMin * this.pxPerMinute}px`,
164
+ height: `${durMin * this.pxPerMinute}px`,
165
+ width: widthExpr,
166
+ left: leftExpr,
167
+ right: 'auto',
168
+ };
169
+ }
170
+ onEventClick(e, mouse) {
171
+ this.eventClick.emit({ event: e, nativeEvent: mouse });
172
+ }
173
+ onCellClick(day, resourceId, mouse) {
174
+ if (this.readonly)
175
+ return;
176
+ const clickedDate = this.computeClickedTime(day, mouse);
177
+ const primaryKey = this.primaryAxis === 'days' ? this.dayKey(day) : resourceId;
178
+ const secondaryKey = this.primaryAxis === 'days' ? resourceId : this.dayKey(day);
179
+ this.slotClick.emit({
180
+ date: clickedDate,
181
+ day: this.startOfDay(day),
182
+ resourceId,
183
+ primaryAxis: this.primaryAxis,
184
+ primaryKey,
185
+ secondaryKey,
186
+ });
187
+ }
188
+ cellDayKey(p, s) {
189
+ return this.dayKey(this.resolveCellDay(p, s));
190
+ }
191
+ cellResourceId(p, s) {
192
+ return this.resolveCellResourceId(p, s);
193
+ }
194
+ resolveCellResourceId(p, s) {
195
+ return this.primaryAxis === 'days' ? s.resource.id : p.resource.id;
196
+ }
197
+ resolveCellResource(p, s) {
198
+ return this.primaryAxis === 'days' ? s.resource : p.resource;
199
+ }
200
+ cellEvents(p, s) {
201
+ const day = this.resolveCellDay(p, s);
202
+ const resourceId = this.resolveCellResourceId(p, s);
203
+ return this.eventsForCell(day, resourceId); // now PositionedEvent[]
204
+ }
205
+ cellClick(p, s, ev) {
206
+ const day = this.resolveCellDay(p, s);
207
+ const resourceId = this.resolveCellResourceId(p, s);
208
+ this.onCellClick(day, resourceId, ev);
209
+ }
210
+ getEventLayoutStyle(e, p, s) {
211
+ const day = this.resolveCellDay(p, s);
212
+ const layout = this.styleForEvent(e, day); // contains top/height/left/width
213
+ const userStyle = this.eventStyle ? this.eventStyle(e) : null;
214
+ // IMPORTANT: layout must win so users can’t break positioning
215
+ return userStyle ? { ...userStyle, ...layout } : layout;
216
+ }
217
+ // ---------- INTERNAL COMPUTATION ----------
218
+ windowBoundsUtc(day) {
219
+ const base = this.startOfDay(day);
220
+ // wall-clock dates for "day at HH:mm" (fields matter)
221
+ const wallStart = this.setTime(base, this.dayStartMinutes);
222
+ const wallEnd = this.setTime(base, this.dayEndMinutes);
223
+ if (!this.timezone || this.timezone === 'local') {
224
+ // interpreted as device local instants
225
+ return { startUtc: wallStart, endUtc: wallEnd };
226
+ }
227
+ if (this.timezone === 'UTC') {
228
+ // build true UTC instants for the wall clock
229
+ const y = base.getFullYear();
230
+ const m = base.getMonth();
231
+ const d = base.getDate();
232
+ const sH = Math.floor(this.dayStartMinutes / 60);
233
+ const sM = this.dayStartMinutes % 60;
234
+ const eH = Math.floor(this.dayEndMinutes / 60);
235
+ const eM = this.dayEndMinutes % 60;
236
+ return {
237
+ startUtc: new Date(Date.UTC(y, m, d, sH, sM, 0, 0)),
238
+ endUtc: new Date(Date.UTC(y, m, d, eH, eM, 0, 0)),
239
+ };
240
+ }
241
+ // IANA timezone: wall-clock in zone -> UTC instant
242
+ return {
243
+ startUtc: fromZonedTime(wallStart, this.timezone),
244
+ endUtc: fromZonedTime(wallEnd, this.timezone),
245
+ };
246
+ }
247
+ assignColumns(cluster) {
248
+ // columnsEndTimes[col] = endTime of last event in that col
249
+ const columnsEndTimes = [];
250
+ const out = [];
251
+ for (const e of cluster) {
252
+ const s = e.start.getTime();
253
+ const en = e.end.getTime();
254
+ // Find first available column
255
+ let col = 0;
256
+ for (; col < columnsEndTimes.length; col++) {
257
+ if (s >= columnsEndTimes[col])
258
+ break;
259
+ }
260
+ if (col === columnsEndTimes.length)
261
+ columnsEndTimes.push(en);
262
+ else
263
+ columnsEndTimes[col] = en;
264
+ out.push({ ...e, _col: col, _cols: 0 });
265
+ }
266
+ const totalCols = columnsEndTimes.length;
267
+ // write total columns for all events in this cluster
268
+ for (const pe of out)
269
+ pe._cols = totalCols;
270
+ return out;
271
+ }
272
+ emitRangeIfChanged(payload) {
273
+ const key = `${payload.start.toISOString()}|${payload.end.toISOString()}|${payload.primaryAxis}|${payload.days}`;
274
+ if (key === this.lastRangeKey)
275
+ return;
276
+ this.lastRangeKey = key;
277
+ queueMicrotask(() => this.rangeChange.emit(payload));
278
+ }
279
+ setStartDate(d) {
280
+ this.startDate = d;
281
+ this.recompute();
282
+ this.startDateChange.emit(d);
283
+ }
284
+ // Computes clicked time from y offset within a cell (very handy for creating events)
285
+ computeClickedTime(day, mouse) {
286
+ const target = mouse.currentTarget;
287
+ if (!target || typeof target.getBoundingClientRect !== 'function') {
288
+ return this.setTime(this.startOfDay(day), this.dayStartMinutes);
289
+ }
290
+ const rect = target.getBoundingClientRect();
291
+ const y = mouse.clientY - rect.top;
292
+ const minutesFromStart = Math.max(0, Math.min(this.dayEndMinutes - this.dayStartMinutes, y / this.pxPerMinute));
293
+ let snapped = minutesFromStart;
294
+ if (this.snapToSlot && this.slotMinutes > 0) {
295
+ snapped = Math.round(minutesFromStart / this.slotMinutes) * this.slotMinutes;
296
+ }
297
+ const totalMinutes = this.dayStartMinutes + snapped;
298
+ return this.setTime(this.startOfDay(day), totalMinutes);
299
+ }
300
+ layoutOverlaps(events) {
301
+ // Sweep through events, grouping overlapping "clusters"
302
+ const positioned = [];
303
+ let cluster = [];
304
+ let clusterEnd = -Infinity;
305
+ const flushCluster = () => {
306
+ if (cluster.length === 0)
307
+ return;
308
+ positioned.push(...this.assignColumns(cluster));
309
+ cluster = [];
310
+ clusterEnd = -Infinity;
311
+ };
312
+ for (const e of events) {
313
+ const s = e.start.getTime();
314
+ const en = e.end.getTime();
315
+ if (cluster.length === 0) {
316
+ cluster = [e];
317
+ clusterEnd = en;
318
+ continue;
319
+ }
320
+ // If this event starts after cluster ends, it's a new cluster
321
+ if (s >= clusterEnd) {
322
+ flushCluster();
323
+ cluster = [e];
324
+ clusterEnd = en;
325
+ continue;
326
+ }
327
+ // Still overlapping cluster
328
+ cluster.push(e);
329
+ if (en > clusterEnd)
330
+ clusterEnd = en;
331
+ }
332
+ flushCluster();
333
+ return positioned;
334
+ }
335
+ resolveCellDay(p, s) {
336
+ return this.primaryAxis === 'days' ? p.day : s.day;
337
+ }
338
+ recompute() {
339
+ // Clamp days
340
+ const d = Math.max(1, Math.min(7, Math.floor(this.nDays || 7)));
341
+ // Parse times
342
+ this.dayStartMinutes = this.parseHmToMinutes(this.dayStart, 8 * 60);
343
+ this.dayEndMinutes = this.parseHmToMinutes(this.dayEnd, 20 * 60);
344
+ if (this.dayEndMinutes <= this.dayStartMinutes) {
345
+ // fallback to a sane window
346
+ this.dayEndMinutes = this.dayStartMinutes + 10 * 60;
347
+ }
348
+ this.slotMinutes = this.parseHmToMinutes(this.slotDuration, 30);
349
+ if (this.slotMinutes <= 0)
350
+ this.slotMinutes = 30;
351
+ // Visible days
352
+ const start = this.startOfDay(this.startDate ?? new Date());
353
+ this.visibleDays = Array.from({ length: d }, (_, i) => this.addDays(start, i));
354
+ // Columns
355
+ if (this.primaryAxis === 'days') {
356
+ this.primaryColumns = this.visibleDays.map((day) => ({
357
+ kind: 'day',
358
+ day,
359
+ key: this.dayKey(day),
360
+ title: this.formatDay(day),
361
+ }));
362
+ this.secondaryColumns = this.resources.map((r) => ({
363
+ kind: 'resource',
364
+ resource: r,
365
+ key: r.id,
366
+ title: r.title,
367
+ }));
368
+ }
369
+ else {
370
+ this.primaryColumns = this.resources.map((r) => ({
371
+ kind: 'resource',
372
+ resource: r,
373
+ key: r.id,
374
+ title: r.title,
375
+ }));
376
+ this.secondaryColumns = this.visibleDays.map((day) => ({
377
+ kind: 'day',
378
+ day,
379
+ key: this.dayKey(day),
380
+ title: this.formatDay(day),
381
+ }));
382
+ }
383
+ // Emit range
384
+ const rangeStart = start;
385
+ const rangeEnd = this.addDays(start, d);
386
+ const view = 'custom-range';
387
+ this.emitRangeIfChanged({
388
+ start: rangeStart,
389
+ end: rangeEnd,
390
+ days: d,
391
+ primaryAxis: this.primaryAxis,
392
+ view,
393
+ });
394
+ }
395
+ // ------------- NAVIGATION -------------
396
+ navigatePrev() {
397
+ const next = this.addDays(this.startOfDay(this.startDate ?? new Date()), -this.normalizedDays());
398
+ this.setStartDate(next);
399
+ }
400
+ navigateNext() {
401
+ const next = this.addDays(this.startOfDay(this.startDate ?? new Date()), this.normalizedDays());
402
+ this.setStartDate(next);
403
+ }
404
+ navigateToday() {
405
+ const today = this.startOfDay(new Date());
406
+ this.setStartDate(today);
407
+ }
408
+ // ---------- DATE/TIME UTILS ----------
409
+ formatHour(h) {
410
+ return `${String(h).padStart(2, '0')}:00`;
411
+ }
412
+ hourTopPx(h) {
413
+ const minutes = (h * 60) - this.dayStartMinutes;
414
+ return minutes * this.pxPerMinute;
415
+ }
416
+ isIanaTz() {
417
+ return !!this.timezone && this.timezone !== 'local' && this.timezone !== 'UTC';
418
+ }
419
+ toDisplayZone(dUtc) {
420
+ if (this.timezone === 'UTC')
421
+ return new Date(dUtc);
422
+ if (this.timezone === 'local' || !this.timezone)
423
+ return new Date(dUtc);
424
+ // Converts a UTC instant to a Date whose wall-clock matches the IANA zone
425
+ return toZonedTime(dUtc, this.timezone);
426
+ }
427
+ fromDisplayZone(dZoned) {
428
+ if (this.timezone === 'UTC')
429
+ return new Date(dZoned);
430
+ if (this.timezone === 'local' || !this.timezone)
431
+ return new Date(dZoned);
432
+ // Converts a wall-clock-in-zone Date back to a UTC instant
433
+ return fromZonedTime(dZoned, this.timezone);
434
+ }
435
+ parseHmToMinutes(hm, fallback) {
436
+ const m = /^(\d{1,2}):(\d{2})$/.exec((hm || '').trim());
437
+ if (!m)
438
+ return fallback;
439
+ const hh = Number(m[1]);
440
+ const mm = Number(m[2]);
441
+ if (!Number.isFinite(hh) || !Number.isFinite(mm))
442
+ return fallback;
443
+ return Math.max(0, Math.min(24 * 60, hh * 60 + mm));
444
+ }
445
+ startOfDay(d) {
446
+ const x = new Date(d);
447
+ x.setHours(0, 0, 0, 0);
448
+ return x;
449
+ }
450
+ addDays(d, n) {
451
+ const x = new Date(d);
452
+ x.setDate(x.getDate() + n);
453
+ return x;
454
+ }
455
+ setTime(day, minutes) {
456
+ const x = new Date(day);
457
+ const hh = Math.floor(minutes / 60);
458
+ const mm = minutes % 60;
459
+ x.setHours(hh, mm, 0, 0);
460
+ return x;
461
+ }
462
+ dayKey(d) {
463
+ // Use YYYY-MM-DD (stable key) instead of full ISO with timezone offsets
464
+ const y = d.getFullYear();
465
+ const m = String(d.getMonth() + 1).padStart(2, '0');
466
+ const day = String(d.getDate()).padStart(2, '0');
467
+ return `${y}-${m}-${day}`;
468
+ }
469
+ formatDay(d) {
470
+ return d.toLocaleDateString(this.locale, { weekday: 'short', month: 'short', day: 'numeric' });
471
+ }
472
+ normalizedDays() {
473
+ const v = Math.floor(this.nDays ?? 7);
474
+ return Math.max(1, Math.min(7, v));
475
+ }
476
+ // ------------- EVENT TOOLTIP -------------
477
+ eventTooltip(e) {
478
+ const start = this.toDisplayZone(e.start);
479
+ const end = this.toDisplayZone(e.end);
480
+ const startStr = start.toLocaleTimeString(this.locale, { hour: '2-digit', minute: '2-digit' });
481
+ const endStr = end.toLocaleTimeString(this.locale, { hour: '2-digit', minute: '2-digit' });
482
+ // keep it short so the native tooltip looks good
483
+ return `${e.title}\n${startStr}–${endStr}`;
484
+ }
485
+ // ------------- CUSTOM EVENT STYLING -----------
486
+ eventTemplateCtx(e, p, s) {
487
+ const day = this.resolveCellDay(p, s);
488
+ const resourceId = this.resolveCellResourceId(p, s);
489
+ return {
490
+ $implicit: e,
491
+ event: e,
492
+ startZoned: this.toDisplayZone(e.start),
493
+ endZoned: this.toDisplayZone(e.end),
494
+ resourceId,
495
+ day,
496
+ };
497
+ }
498
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgxResourceSchedulerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
499
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NgxResourceSchedulerComponent, isStandalone: false, selector: "ngx-resource-scheduler", inputs: { startDate: "startDate", resources: "resources", events: "events", nDays: "nDays", primaryAxis: "primaryAxis", dayStart: "dayStart", dayEnd: "dayEnd", slotDuration: "slotDuration", snapToSlot: "snapToSlot", showSlotLines: "showSlotLines", slotLineStyle: "slotLineStyle", showToolbar: "showToolbar", prevLabel: "prevLabel", nextLabel: "nextLabel", eventTemplate: "eventTemplate", eventClass: "eventClass", eventStyle: "eventStyle", showDaysResourcesLabel: "showDaysResourcesLabel", daysLabel: "daysLabel", resourcesLabel: "resourcesLabel", todayLabel: "todayLabel", locale: "locale", timezone: "timezone", readonly: "readonly" }, outputs: { slotClick: "slotClick", eventClick: "eventClick", eventChange: "eventChange", rangeChange: "rangeChange", startDateChange: "startDateChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"ngx-scheduler\">\n\n <!-- Toolbar -->\n @if (showToolbar) {\n <div class=\"ngx-toolbar\">\n <div class=\"ngx-toolbar-left\">\n <button type=\"button\" class=\"ngx-btn\" (click)=\"navigatePrev()\">{{ prevLabel }}</button>\n <button type=\"button\" class=\"ngx-btn ngx-btn--ghost\" (click)=\"navigateToday()\">{{ todayLabel }}</button>\n <button type=\"button\" class=\"ngx-btn\" (click)=\"navigateNext()\">{{ nextLabel }}</button>\n </div>\n <div class=\"ngx-toolbar-title\">\n {{ rangeTitle }}\n </div>\n <div class=\"ngx-toolbar-right\">\n @if (showDaysResourcesLabel) {\n <span class=\"ngx-toolbar-meta\">\n {{ primaryAxis === 'days' ? (visibleDays.length + ' ' + daysLabel) : (resources.length + ' ' + resourcesLabel ) }}\n </span>\n }\n @if (!showDaysResourcesLabel) {\n <span class=\"ngx-toolbar-meta\">&nbsp;</span>\n }\n </div>\n </div>\n }\n\n <!-- Header row -->\n <div class=\"ngx-header\">\n <div class=\"ngx-time-gutter\"></div>\n\n <div class=\"ngx-primary-headers\" [style.gridTemplateColumns]=\"'repeat(' + primaryColumns.length + ', minmax(0, 1fr))'\">\n @for (p of primaryColumns; track trackPrimary($index, p)) {\n <div class=\"ngx-primary-header\">\n <div class=\"ngx-primary-title-row\">\n <div class=\"ngx-primary-title\">{{ p.title }}</div>\n </div>\n <div class=\"ngx-secondary-header-row\" [style.gridTemplateColumns]=\"'repeat(' + secondaryColumns.length + ', minmax(0, 1fr))'\">\n @for (s of secondaryColumns; track trackSecondary($index, s)) {\n <div class=\"ngx-secondary-header\">\n <span class=\"ngx-secondary-header-title\">{{ s.title }}</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Body -->\n <div class=\"ngx-body\">\n <!-- time gutter -->\n <div class=\"ngx-time-gutter\" [style.height.px]=\"timelineHeightPx\">\n @for (h of hourLabels; track h) {\n <div\n class=\"ngx-hour-label\"\n [style.top.px]=\"hourTopPx(h)\">\n {{ formatHour(h) }}\n </div>\n }\n </div>\n\n\n <!-- grid -->\n <div class=\"ngx-grid\"\n [style.gridTemplateColumns]=\"'repeat(' + primaryColumns.length + ', minmax(0, 1fr))'\"\n [style.height.px]=\"timelineHeightPx\">\n\n @for (p of primaryColumns; track trackPrimary($index, p)) {\n <div class=\"ngx-primary-col\">\n <div class=\"ngx-secondary-cols\"\n [style.gridTemplateColumns]=\"'repeat(' + secondaryColumns.length + ', minmax(0, 1fr))'\"\n [style.height.px]=\"timelineHeightPx\">\n <!-- Each cell is (day, resource) regardless of axis order -->\n @for (s of secondaryColumns; track trackSecondary($index, s)) {\n <div class=\"ngx-cell\"\n #cellEl\n [style.height.px]=\"timelineHeightPx\"\n (click)=\"cellClick(p, s, $event)\"\n [attr.data-day]=\"cellDayKey(p,s)\"\n [attr.data-resource]=\"cellResourceId(p,s)\">\n <!-- grid lines -->\n <div class=\"ngx-lines\" aria-hidden=\"true\">\n <!-- slot lines -->\n @for (line of slotLines; track line) {\n <div\n class=\"ngx-line ngx-line--slot\"\n [style.top.px]=\"line.top\"\n [class.ngx-line--half]=\"line.isHalfHour\"\n [class.is-hidden]=\"slotLineStyle === 'hour'\">\n </div>\n }\n <!-- hour lines -->\n @for (top of hourLineOffsetsPx; track top) {\n <div\n class=\"ngx-line ngx-line--hour\"\n [style.top.px]=\"top\"\n [class.is-hidden]=\"slotLineStyle === 'slot'\">\n </div>\n }\n </div>\n <!-- events -->\n <!-- ngClass and 2nd ngStyle are only used when user passes custom class/style -->\n @for (e of cellEvents(p, s); track trackEvent($index, e)) {\n <div class=\"ngx-event\"\n [ngStyle]=\"getEventLayoutStyle(e, p, s)\"\n (click)=\"onEventClick(e, $event); $event.stopPropagation()\"\n [ngClass]=\"eventClass ? eventClass(e) : null\"\n [attr.title]=\"eventTooltip(e)\">\n @if (eventTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"eventTemplate\"\n [ngTemplateOutletContext]=\"eventTemplateCtx(e, p, s)\">\n </ng-container>\n } @else {\n <div class=\"ngx-event-title\">{{ e.title }}</div>\n <div class=\"ngx-event-time\">\n {{ toDisplayZone(e.start) | date:'HH:mm' }}\u2013{{ toDisplayZone(e.end) | date:'HH:mm' }}\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n </div>\n </div>\n\n</div>\n", styles: [":host{display:block;--ngx-bg: #ffffff;--ngx-surface: #ffffff;--ngx-muted: #f6f7f9;--ngx-border: rgba(16, 24, 40, .1);--ngx-border-soft: rgba(16, 24, 40, .06);--ngx-text: #101828;--ngx-text-muted: rgba(16, 24, 40, .6);--ngx-radius: 6px;--ngx-shadow: 0 10px 25px rgba(16, 24, 40, .06);--ngx-shadow-soft: 0 8px 18px rgba(16, 24, 40, .05);--ngx-event-bg: #eef4ff;--ngx-event-border: rgba(53, 122, 246, .25);--ngx-event-text: #0b1f44;--ngx-time-gutter-width: 72px;--ngx-header-height: 78px;--ngx-primary-title-height: 40px;--ngx-secondary-title-height: 45px;background:var(--ngx-bg);color:var(--ngx-text);--ngx-grid-gap: 5px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-weight:400;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Inter,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"}.ngx-scheduler{display:grid;grid-template-rows:auto 1fr;gap:var(--ngx-grid-gap)}.ngx-toolbar{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;padding:10px 10px 2px}.ngx-toolbar-title{text-align:center;font-weight:650;font-size:14px;color:var(--ngx-text);letter-spacing:.2px}.ngx-toolbar-meta{font-size:12px;color:var(--ngx-text-muted);padding-right:6px}.ngx-toolbar-left{display:flex;gap:8px;align-items:center}.ngx-btn{height:34px;padding:0 10px;border-radius:12px;border:1px solid var(--ngx-border);background:#fff;color:var(--ngx-text);font-weight:600;font-size:12px;cursor:pointer;box-shadow:0 8px 14px #1018280f;transition:transform .12s ease,box-shadow .12s ease,background .12s ease}.ngx-btn:hover{transform:translateY(-1px);box-shadow:0 12px 18px #1018281a}.ngx-btn:active{transform:translateY(0);box-shadow:0 8px 14px #10182814}.ngx-btn--ghost{background:var(--ngx-muted)}.ngx-header{position:sticky;top:0;z-index:5;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);gap:var(--ngx-grid-gap);display:grid;grid-template-columns:var(--ngx-time-gutter-width) 1fr;align-items:stretch;background:var(--ngx-bg)}.ngx-header .ngx-time-gutter{background:transparent}.ngx-primary-headers{display:grid;gap:var(--ngx-grid-gap)}.ngx-primary-header{border:1px solid var(--ngx-border);border-radius:var(--ngx-radius);overflow:hidden;background:var(--ngx-surface);box-shadow:var(--ngx-shadow-soft)}.ngx-primary-title-row{height:var(--ngx-primary-title-height);display:flex;align-items:center;padding:0 12px;font-weight:650;font-size:13px;letter-spacing:.2px;border-bottom:1px solid var(--ngx-border-soft);background:linear-gradient(to bottom,#fff,#fbfbfc)}.ngx-primary-title-row .ngx-primary-title{margin:auto;text-align:center}.ngx-secondary-header-row{height:var(--ngx-secondary-title-height);display:grid}.ngx-secondary-header{display:flex;align-items:center;padding:0 6px;font-size:12px;text-align:center;color:var(--ngx-text-muted);border-left:1px solid var(--ngx-border-soft);background:#fff}.ngx-secondary-header-title{margin:auto}.ngx-secondary-header:first-child{border-left:none}.ngx-body{display:grid;grid-template-columns:var(--ngx-time-gutter-width) 1fr;gap:10px;min-height:320px}.ngx-time-gutter{position:relative;-webkit-user-select:none;user-select:none}.ngx-hour-label{position:absolute;left:0;right:8px;transform:translateY(-50%);text-align:right;font-size:12px;color:var(--ngx-text-muted);pointer-events:none}.ngx-hour-label:first-child{transform:translateY(0);top:0!important}.ngx-hour-label:last-child{transform:translateY(-100%)}.ngx-grid{position:relative;display:grid;gap:var(--ngx-grid-gap)}.ngx-primary-col{border:1px solid var(--ngx-border);border-radius:var(--ngx-radius);overflow:hidden;background:var(--ngx-surface);box-shadow:var(--ngx-shadow)}.ngx-secondary-cols{display:grid;height:100%}.ngx-cell{position:relative;border-left:1px solid var(--ngx-border-soft);background:#fff;cursor:pointer;transition:background .12s ease}.ngx-cell .ngx-event{box-sizing:border-box}.ngx-cell:first-child{border-left:none}.ngx-cell:hover{background:#10182805}.ngx-lines{position:absolute;inset:0;pointer-events:none}.ngx-line{position:absolute;left:0;right:0;height:0;border-top:1px solid var(--ngx-border-soft);opacity:.75}.ngx-line--slot{opacity:.5}.ngx-line--half{opacity:.7;border-top-color:#10182817}.ngx-line--hour{opacity:.9;border-top-color:#1018281a}.is-hidden{display:none}.ngx-event{display:flex;flex-direction:column;gap:2px;touch-action:none;-webkit-user-select:none;user-select:none;overflow:hidden;position:absolute;min-width:0;margin:0;border-radius:4px;background:var(--ngx-event-bg);border:1px solid var(--ngx-event-border);color:var(--ngx-event-text);padding:2px 4px;box-sizing:border-box;box-shadow:0 10px 18px #10182814;cursor:pointer;transition:transform .12s ease,box-shadow .12s ease,filter .12s ease}.ngx-event:hover{transform:translateY(-1px);box-shadow:0 14px 22px #1018281f;filter:saturate(1.05)}.ngx-event:hover .ngx-resize{opacity:1}.ngx-event:active{transform:translateY(0);box-shadow:0 10px 18px #1018281a;cursor:grabbing}.ngx-event-title{font-weight:650;font-size:11px;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ngx-event-time{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:3px;font-size:10px;color:#0b1f44b8}.ngx-event-time,.ngx-toolbar-meta,.ngx-secondary-header{letter-spacing:.01em}.ngx-hour,.ngx-event-time{font-variant-numeric:tabular-nums}.ngx-body{overflow:auto;padding-bottom:4px;gap:var(--ngx-grid-gap)}.ngx-body::-webkit-scrollbar{width:10px;height:10px}.ngx-body::-webkit-scrollbar-thumb{background:#10182824;border-radius:10px;border:2px solid rgba(255,255,255,.9)}.ngx-body::-webkit-scrollbar-track{background:#1018280a;border-radius:10px}@media(max-width:900px){:host{--ngx-time-gutter-width: 58px}.ngx-primary-headers,.ngx-grid{display:grid;gap:10px}.ngx-primary-headers{padding-left:calc(var(--ngx-grid-gap) / 2);padding-right:calc(var(--ngx-grid-gap) / 2)}.ngx-event{left:6px;right:6px;border-radius:10px}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
500
+ }
501
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgxResourceSchedulerComponent, decorators: [{
502
+ type: Component,
503
+ args: [{ selector: 'ngx-resource-scheduler', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"ngx-scheduler\">\n\n <!-- Toolbar -->\n @if (showToolbar) {\n <div class=\"ngx-toolbar\">\n <div class=\"ngx-toolbar-left\">\n <button type=\"button\" class=\"ngx-btn\" (click)=\"navigatePrev()\">{{ prevLabel }}</button>\n <button type=\"button\" class=\"ngx-btn ngx-btn--ghost\" (click)=\"navigateToday()\">{{ todayLabel }}</button>\n <button type=\"button\" class=\"ngx-btn\" (click)=\"navigateNext()\">{{ nextLabel }}</button>\n </div>\n <div class=\"ngx-toolbar-title\">\n {{ rangeTitle }}\n </div>\n <div class=\"ngx-toolbar-right\">\n @if (showDaysResourcesLabel) {\n <span class=\"ngx-toolbar-meta\">\n {{ primaryAxis === 'days' ? (visibleDays.length + ' ' + daysLabel) : (resources.length + ' ' + resourcesLabel ) }}\n </span>\n }\n @if (!showDaysResourcesLabel) {\n <span class=\"ngx-toolbar-meta\">&nbsp;</span>\n }\n </div>\n </div>\n }\n\n <!-- Header row -->\n <div class=\"ngx-header\">\n <div class=\"ngx-time-gutter\"></div>\n\n <div class=\"ngx-primary-headers\" [style.gridTemplateColumns]=\"'repeat(' + primaryColumns.length + ', minmax(0, 1fr))'\">\n @for (p of primaryColumns; track trackPrimary($index, p)) {\n <div class=\"ngx-primary-header\">\n <div class=\"ngx-primary-title-row\">\n <div class=\"ngx-primary-title\">{{ p.title }}</div>\n </div>\n <div class=\"ngx-secondary-header-row\" [style.gridTemplateColumns]=\"'repeat(' + secondaryColumns.length + ', minmax(0, 1fr))'\">\n @for (s of secondaryColumns; track trackSecondary($index, s)) {\n <div class=\"ngx-secondary-header\">\n <span class=\"ngx-secondary-header-title\">{{ s.title }}</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Body -->\n <div class=\"ngx-body\">\n <!-- time gutter -->\n <div class=\"ngx-time-gutter\" [style.height.px]=\"timelineHeightPx\">\n @for (h of hourLabels; track h) {\n <div\n class=\"ngx-hour-label\"\n [style.top.px]=\"hourTopPx(h)\">\n {{ formatHour(h) }}\n </div>\n }\n </div>\n\n\n <!-- grid -->\n <div class=\"ngx-grid\"\n [style.gridTemplateColumns]=\"'repeat(' + primaryColumns.length + ', minmax(0, 1fr))'\"\n [style.height.px]=\"timelineHeightPx\">\n\n @for (p of primaryColumns; track trackPrimary($index, p)) {\n <div class=\"ngx-primary-col\">\n <div class=\"ngx-secondary-cols\"\n [style.gridTemplateColumns]=\"'repeat(' + secondaryColumns.length + ', minmax(0, 1fr))'\"\n [style.height.px]=\"timelineHeightPx\">\n <!-- Each cell is (day, resource) regardless of axis order -->\n @for (s of secondaryColumns; track trackSecondary($index, s)) {\n <div class=\"ngx-cell\"\n #cellEl\n [style.height.px]=\"timelineHeightPx\"\n (click)=\"cellClick(p, s, $event)\"\n [attr.data-day]=\"cellDayKey(p,s)\"\n [attr.data-resource]=\"cellResourceId(p,s)\">\n <!-- grid lines -->\n <div class=\"ngx-lines\" aria-hidden=\"true\">\n <!-- slot lines -->\n @for (line of slotLines; track line) {\n <div\n class=\"ngx-line ngx-line--slot\"\n [style.top.px]=\"line.top\"\n [class.ngx-line--half]=\"line.isHalfHour\"\n [class.is-hidden]=\"slotLineStyle === 'hour'\">\n </div>\n }\n <!-- hour lines -->\n @for (top of hourLineOffsetsPx; track top) {\n <div\n class=\"ngx-line ngx-line--hour\"\n [style.top.px]=\"top\"\n [class.is-hidden]=\"slotLineStyle === 'slot'\">\n </div>\n }\n </div>\n <!-- events -->\n <!-- ngClass and 2nd ngStyle are only used when user passes custom class/style -->\n @for (e of cellEvents(p, s); track trackEvent($index, e)) {\n <div class=\"ngx-event\"\n [ngStyle]=\"getEventLayoutStyle(e, p, s)\"\n (click)=\"onEventClick(e, $event); $event.stopPropagation()\"\n [ngClass]=\"eventClass ? eventClass(e) : null\"\n [attr.title]=\"eventTooltip(e)\">\n @if (eventTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"eventTemplate\"\n [ngTemplateOutletContext]=\"eventTemplateCtx(e, p, s)\">\n </ng-container>\n } @else {\n <div class=\"ngx-event-title\">{{ e.title }}</div>\n <div class=\"ngx-event-time\">\n {{ toDisplayZone(e.start) | date:'HH:mm' }}\u2013{{ toDisplayZone(e.end) | date:'HH:mm' }}\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n </div>\n </div>\n\n</div>\n", styles: [":host{display:block;--ngx-bg: #ffffff;--ngx-surface: #ffffff;--ngx-muted: #f6f7f9;--ngx-border: rgba(16, 24, 40, .1);--ngx-border-soft: rgba(16, 24, 40, .06);--ngx-text: #101828;--ngx-text-muted: rgba(16, 24, 40, .6);--ngx-radius: 6px;--ngx-shadow: 0 10px 25px rgba(16, 24, 40, .06);--ngx-shadow-soft: 0 8px 18px rgba(16, 24, 40, .05);--ngx-event-bg: #eef4ff;--ngx-event-border: rgba(53, 122, 246, .25);--ngx-event-text: #0b1f44;--ngx-time-gutter-width: 72px;--ngx-header-height: 78px;--ngx-primary-title-height: 40px;--ngx-secondary-title-height: 45px;background:var(--ngx-bg);color:var(--ngx-text);--ngx-grid-gap: 5px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-weight:400;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Inter,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"}.ngx-scheduler{display:grid;grid-template-rows:auto 1fr;gap:var(--ngx-grid-gap)}.ngx-toolbar{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;padding:10px 10px 2px}.ngx-toolbar-title{text-align:center;font-weight:650;font-size:14px;color:var(--ngx-text);letter-spacing:.2px}.ngx-toolbar-meta{font-size:12px;color:var(--ngx-text-muted);padding-right:6px}.ngx-toolbar-left{display:flex;gap:8px;align-items:center}.ngx-btn{height:34px;padding:0 10px;border-radius:12px;border:1px solid var(--ngx-border);background:#fff;color:var(--ngx-text);font-weight:600;font-size:12px;cursor:pointer;box-shadow:0 8px 14px #1018280f;transition:transform .12s ease,box-shadow .12s ease,background .12s ease}.ngx-btn:hover{transform:translateY(-1px);box-shadow:0 12px 18px #1018281a}.ngx-btn:active{transform:translateY(0);box-shadow:0 8px 14px #10182814}.ngx-btn--ghost{background:var(--ngx-muted)}.ngx-header{position:sticky;top:0;z-index:5;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);gap:var(--ngx-grid-gap);display:grid;grid-template-columns:var(--ngx-time-gutter-width) 1fr;align-items:stretch;background:var(--ngx-bg)}.ngx-header .ngx-time-gutter{background:transparent}.ngx-primary-headers{display:grid;gap:var(--ngx-grid-gap)}.ngx-primary-header{border:1px solid var(--ngx-border);border-radius:var(--ngx-radius);overflow:hidden;background:var(--ngx-surface);box-shadow:var(--ngx-shadow-soft)}.ngx-primary-title-row{height:var(--ngx-primary-title-height);display:flex;align-items:center;padding:0 12px;font-weight:650;font-size:13px;letter-spacing:.2px;border-bottom:1px solid var(--ngx-border-soft);background:linear-gradient(to bottom,#fff,#fbfbfc)}.ngx-primary-title-row .ngx-primary-title{margin:auto;text-align:center}.ngx-secondary-header-row{height:var(--ngx-secondary-title-height);display:grid}.ngx-secondary-header{display:flex;align-items:center;padding:0 6px;font-size:12px;text-align:center;color:var(--ngx-text-muted);border-left:1px solid var(--ngx-border-soft);background:#fff}.ngx-secondary-header-title{margin:auto}.ngx-secondary-header:first-child{border-left:none}.ngx-body{display:grid;grid-template-columns:var(--ngx-time-gutter-width) 1fr;gap:10px;min-height:320px}.ngx-time-gutter{position:relative;-webkit-user-select:none;user-select:none}.ngx-hour-label{position:absolute;left:0;right:8px;transform:translateY(-50%);text-align:right;font-size:12px;color:var(--ngx-text-muted);pointer-events:none}.ngx-hour-label:first-child{transform:translateY(0);top:0!important}.ngx-hour-label:last-child{transform:translateY(-100%)}.ngx-grid{position:relative;display:grid;gap:var(--ngx-grid-gap)}.ngx-primary-col{border:1px solid var(--ngx-border);border-radius:var(--ngx-radius);overflow:hidden;background:var(--ngx-surface);box-shadow:var(--ngx-shadow)}.ngx-secondary-cols{display:grid;height:100%}.ngx-cell{position:relative;border-left:1px solid var(--ngx-border-soft);background:#fff;cursor:pointer;transition:background .12s ease}.ngx-cell .ngx-event{box-sizing:border-box}.ngx-cell:first-child{border-left:none}.ngx-cell:hover{background:#10182805}.ngx-lines{position:absolute;inset:0;pointer-events:none}.ngx-line{position:absolute;left:0;right:0;height:0;border-top:1px solid var(--ngx-border-soft);opacity:.75}.ngx-line--slot{opacity:.5}.ngx-line--half{opacity:.7;border-top-color:#10182817}.ngx-line--hour{opacity:.9;border-top-color:#1018281a}.is-hidden{display:none}.ngx-event{display:flex;flex-direction:column;gap:2px;touch-action:none;-webkit-user-select:none;user-select:none;overflow:hidden;position:absolute;min-width:0;margin:0;border-radius:4px;background:var(--ngx-event-bg);border:1px solid var(--ngx-event-border);color:var(--ngx-event-text);padding:2px 4px;box-sizing:border-box;box-shadow:0 10px 18px #10182814;cursor:pointer;transition:transform .12s ease,box-shadow .12s ease,filter .12s ease}.ngx-event:hover{transform:translateY(-1px);box-shadow:0 14px 22px #1018281f;filter:saturate(1.05)}.ngx-event:hover .ngx-resize{opacity:1}.ngx-event:active{transform:translateY(0);box-shadow:0 10px 18px #1018281a;cursor:grabbing}.ngx-event-title{font-weight:650;font-size:11px;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ngx-event-time{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:3px;font-size:10px;color:#0b1f44b8}.ngx-event-time,.ngx-toolbar-meta,.ngx-secondary-header{letter-spacing:.01em}.ngx-hour,.ngx-event-time{font-variant-numeric:tabular-nums}.ngx-body{overflow:auto;padding-bottom:4px;gap:var(--ngx-grid-gap)}.ngx-body::-webkit-scrollbar{width:10px;height:10px}.ngx-body::-webkit-scrollbar-thumb{background:#10182824;border-radius:10px;border:2px solid rgba(255,255,255,.9)}.ngx-body::-webkit-scrollbar-track{background:#1018280a;border-radius:10px}@media(max-width:900px){:host{--ngx-time-gutter-width: 58px}.ngx-primary-headers,.ngx-grid{display:grid;gap:10px}.ngx-primary-headers{padding-left:calc(var(--ngx-grid-gap) / 2);padding-right:calc(var(--ngx-grid-gap) / 2)}.ngx-event{left:6px;right:6px;border-radius:10px}}\n"] }]
504
+ }], propDecorators: { startDate: [{
505
+ type: Input
506
+ }], resources: [{
507
+ type: Input
508
+ }], events: [{
509
+ type: Input
510
+ }], nDays: [{
511
+ type: Input
512
+ }], primaryAxis: [{
513
+ type: Input
514
+ }], dayStart: [{
515
+ type: Input
516
+ }], dayEnd: [{
517
+ type: Input
518
+ }], slotDuration: [{
519
+ type: Input
520
+ }], snapToSlot: [{
521
+ type: Input
522
+ }], showSlotLines: [{
523
+ type: Input
524
+ }], slotLineStyle: [{
525
+ type: Input
526
+ }], showToolbar: [{
527
+ type: Input
528
+ }], prevLabel: [{
529
+ type: Input
530
+ }], nextLabel: [{
531
+ type: Input
532
+ }], eventTemplate: [{
533
+ type: Input
534
+ }], eventClass: [{
535
+ type: Input
536
+ }], eventStyle: [{
537
+ type: Input
538
+ }], showDaysResourcesLabel: [{
539
+ type: Input
540
+ }], daysLabel: [{
541
+ type: Input
542
+ }], resourcesLabel: [{
543
+ type: Input
544
+ }], todayLabel: [{
545
+ type: Input
546
+ }], locale: [{
547
+ type: Input
548
+ }], timezone: [{
549
+ type: Input
550
+ }], readonly: [{
551
+ type: Input
552
+ }], slotClick: [{
553
+ type: Output
554
+ }], eventClick: [{
555
+ type: Output
556
+ }], eventChange: [{
557
+ type: Output
558
+ }], rangeChange: [{
559
+ type: Output
560
+ }], startDateChange: [{
561
+ type: Output
562
+ }] } });
563
+
564
+ /**
565
+ * Public types for ngx-resource-scheduler
566
+ * Keep this file framework-agnostic (no Angular imports).
567
+ */
568
+
569
+ class NgxResourceSchedulerModule {
570
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgxResourceSchedulerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
571
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: NgxResourceSchedulerModule, declarations: [NgxResourceSchedulerComponent], imports: [CommonModule], exports: [NgxResourceSchedulerComponent] }); }
572
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgxResourceSchedulerModule, imports: [CommonModule] }); }
573
+ }
574
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgxResourceSchedulerModule, decorators: [{
575
+ type: NgModule,
576
+ args: [{
577
+ imports: [CommonModule],
578
+ declarations: [NgxResourceSchedulerComponent],
579
+ exports: [NgxResourceSchedulerComponent],
580
+ }]
581
+ }] });
582
+
583
+ /*
584
+ * Public API Surface of ngx-resource-scheduler
585
+ */
586
+
587
+ /**
588
+ * Generated bundle index. Do not edit.
589
+ */
590
+
591
+ export { NgxResourceSchedulerComponent, NgxResourceSchedulerModule };
592
+ //# sourceMappingURL=ngx-resource-scheduler.mjs.map