@memberjunction/ng-timeline 2.122.1 → 2.123.0
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 +385 -245
- package/dist/lib/component/timeline.component.d.ts +404 -60
- package/dist/lib/component/timeline.component.d.ts.map +1 -1
- package/dist/lib/component/timeline.component.js +2029 -132
- package/dist/lib/component/timeline.component.js.map +1 -1
- package/dist/lib/events.d.ts +440 -0
- package/dist/lib/events.d.ts.map +1 -0
- package/dist/lib/events.js +11 -0
- package/dist/lib/events.js.map +1 -0
- package/dist/lib/module.d.ts +14 -6
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/module.js +24 -26
- package/dist/lib/module.js.map +1 -1
- package/dist/lib/timeline-group.d.ts +387 -0
- package/dist/lib/timeline-group.d.ts.map +1 -0
- package/dist/lib/timeline-group.js +523 -0
- package/dist/lib/timeline-group.js.map +1 -0
- package/dist/lib/types.d.ts +491 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +49 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/public-api.d.ts +12 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +15 -2
- package/dist/public-api.js.map +1 -1
- package/package.json +19 -17
|
@@ -1,200 +1,2097 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview MJ Timeline Component - A flexible, responsive timeline for Angular.
|
|
3
|
+
*
|
|
4
|
+
* This component displays chronological data in a timeline format with support for:
|
|
5
|
+
* - Multiple data sources (MemberJunction entities or plain objects)
|
|
6
|
+
* - Virtual scrolling for large datasets
|
|
7
|
+
* - Collapsible time segments
|
|
8
|
+
* - Rich event system with BeforeX/AfterX pattern
|
|
9
|
+
* - Full keyboard navigation and accessibility
|
|
10
|
+
* - Responsive design for all screen sizes
|
|
11
|
+
*
|
|
12
|
+
* @module @memberjunction/ng-timeline
|
|
13
|
+
*/
|
|
14
|
+
import { Component, Input, Output, EventEmitter, ViewChild, ContentChild, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
|
|
15
|
+
import { Subject } from 'rxjs';
|
|
16
|
+
import { takeUntil, debounceTime } from 'rxjs/operators';
|
|
17
|
+
import { DEFAULT_CARD_CONFIG, DEFAULT_VIRTUAL_SCROLL_CONFIG, DEFAULT_VIRTUAL_SCROLL_STATE } from '../types';
|
|
18
|
+
import { getFieldValue } from '../timeline-group';
|
|
3
19
|
import * as i0 from "@angular/core";
|
|
4
|
-
import * as i1 from "@
|
|
20
|
+
import * as i1 from "@angular/common";
|
|
21
|
+
const _c0 = ["cardTemplate"];
|
|
22
|
+
const _c1 = ["headerTemplate"];
|
|
23
|
+
const _c2 = ["bodyTemplate"];
|
|
24
|
+
const _c3 = ["actionsTemplate"];
|
|
25
|
+
const _c4 = ["segmentHeaderTemplate"];
|
|
26
|
+
const _c5 = ["emptyTemplate"];
|
|
27
|
+
const _c6 = ["loadingTemplate"];
|
|
28
|
+
const _c7 = ["scrollContainer"];
|
|
29
|
+
const _c8 = a0 => ({ segment: a0 });
|
|
30
|
+
const _c9 = (a0, a1, a2, a3) => ({ event: a0, index: a1, isOdd: a2, globalIndex: a3 });
|
|
31
|
+
const _c10 = (a0, a1) => ({ event: a0, group: a1 });
|
|
32
|
+
const _c11 = a0 => ({ event: a0 });
|
|
33
|
+
const _c12 = (a0, a1) => ({ event: a0, actions: a1 });
|
|
34
|
+
function TimelineComponent_ng_container_2_ng_container_1_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
35
|
+
i0.ɵɵelementContainer(0);
|
|
36
|
+
} }
|
|
37
|
+
function TimelineComponent_ng_container_2_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
38
|
+
i0.ɵɵelementContainerStart(0);
|
|
39
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_2_ng_container_1_ng_container_1_Template, 1, 0, "ng-container", 15);
|
|
40
|
+
i0.ɵɵelementContainerEnd();
|
|
41
|
+
} if (rf & 2) {
|
|
42
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
43
|
+
i0.ɵɵadvance();
|
|
44
|
+
i0.ɵɵproperty("ngTemplateOutlet", ctx_r1.loadingTemplate);
|
|
45
|
+
} }
|
|
46
|
+
function TimelineComponent_ng_container_2_ng_template_2_Template(rf, ctx) { if (rf & 1) {
|
|
47
|
+
i0.ɵɵelementStart(0, "div", 16);
|
|
48
|
+
i0.ɵɵelement(1, "div", 17);
|
|
49
|
+
i0.ɵɵelementStart(2, "span", 18);
|
|
50
|
+
i0.ɵɵtext(3);
|
|
51
|
+
i0.ɵɵelementEnd()();
|
|
52
|
+
} if (rf & 2) {
|
|
53
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
54
|
+
i0.ɵɵadvance(3);
|
|
55
|
+
i0.ɵɵtextInterpolate(ctx_r1.loadingMessage);
|
|
56
|
+
} }
|
|
57
|
+
function TimelineComponent_ng_container_2_Template(rf, ctx) { if (rf & 1) {
|
|
58
|
+
i0.ɵɵelementContainerStart(0);
|
|
59
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_2_ng_container_1_Template, 2, 1, "ng-container", 14)(2, TimelineComponent_ng_container_2_ng_template_2_Template, 4, 1, "ng-template", null, 2, i0.ɵɵtemplateRefExtractor);
|
|
60
|
+
i0.ɵɵelementContainerEnd();
|
|
61
|
+
} if (rf & 2) {
|
|
62
|
+
const defaultLoading_r3 = i0.ɵɵreference(3);
|
|
63
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
64
|
+
i0.ɵɵadvance();
|
|
65
|
+
i0.ɵɵproperty("ngIf", ctx_r1.loadingTemplate)("ngIfElse", defaultLoading_r3);
|
|
66
|
+
} }
|
|
67
|
+
function TimelineComponent_ng_container_3_ng_container_1_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
68
|
+
i0.ɵɵelementContainer(0);
|
|
69
|
+
} }
|
|
70
|
+
function TimelineComponent_ng_container_3_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
71
|
+
i0.ɵɵelementContainerStart(0);
|
|
72
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_3_ng_container_1_ng_container_1_Template, 1, 0, "ng-container", 15);
|
|
73
|
+
i0.ɵɵelementContainerEnd();
|
|
74
|
+
} if (rf & 2) {
|
|
75
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
76
|
+
i0.ɵɵadvance();
|
|
77
|
+
i0.ɵɵproperty("ngTemplateOutlet", ctx_r1.emptyTemplate);
|
|
78
|
+
} }
|
|
79
|
+
function TimelineComponent_ng_container_3_ng_template_2_Template(rf, ctx) { if (rf & 1) {
|
|
80
|
+
i0.ɵɵelementStart(0, "div", 19);
|
|
81
|
+
i0.ɵɵelement(1, "i", 20);
|
|
82
|
+
i0.ɵɵelementStart(2, "span", 21);
|
|
83
|
+
i0.ɵɵtext(3);
|
|
84
|
+
i0.ɵɵelementEnd()();
|
|
85
|
+
} if (rf & 2) {
|
|
86
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
87
|
+
i0.ɵɵadvance();
|
|
88
|
+
i0.ɵɵclassMap(ctx_r1.emptyIcon);
|
|
89
|
+
i0.ɵɵadvance(2);
|
|
90
|
+
i0.ɵɵtextInterpolate(ctx_r1.emptyMessage);
|
|
91
|
+
} }
|
|
92
|
+
function TimelineComponent_ng_container_3_Template(rf, ctx) { if (rf & 1) {
|
|
93
|
+
i0.ɵɵelementContainerStart(0);
|
|
94
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_3_ng_container_1_Template, 2, 1, "ng-container", 14)(2, TimelineComponent_ng_container_3_ng_template_2_Template, 4, 3, "ng-template", null, 3, i0.ɵɵtemplateRefExtractor);
|
|
95
|
+
i0.ɵɵelementContainerEnd();
|
|
96
|
+
} if (rf & 2) {
|
|
97
|
+
const defaultEmpty_r4 = i0.ɵɵreference(3);
|
|
98
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
99
|
+
i0.ɵɵadvance();
|
|
100
|
+
i0.ɵɵproperty("ngIf", ctx_r1.emptyTemplate)("ngIfElse", defaultEmpty_r4);
|
|
101
|
+
} }
|
|
102
|
+
function TimelineComponent_ng_container_4_ng_container_1_div_1_ng_container_2_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
103
|
+
i0.ɵɵelementContainer(0);
|
|
104
|
+
} }
|
|
105
|
+
function TimelineComponent_ng_container_4_ng_container_1_div_1_ng_container_2_Template(rf, ctx) { if (rf & 1) {
|
|
106
|
+
i0.ɵɵelementContainerStart(0);
|
|
107
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_4_ng_container_1_div_1_ng_container_2_ng_container_1_Template, 1, 0, "ng-container", 28);
|
|
108
|
+
i0.ɵɵelementContainerEnd();
|
|
109
|
+
} if (rf & 2) {
|
|
110
|
+
const segment_r6 = i0.ɵɵnextContext().$implicit;
|
|
111
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
112
|
+
i0.ɵɵadvance();
|
|
113
|
+
i0.ɵɵproperty("ngTemplateOutlet", ctx_r1.segmentHeaderTemplate)("ngTemplateOutletContext", i0.ɵɵpureFunction1(2, _c8, segment_r6));
|
|
114
|
+
} }
|
|
115
|
+
function TimelineComponent_ng_container_4_ng_container_1_div_1_ng_template_3_span_0_Template(rf, ctx) { if (rf & 1) {
|
|
116
|
+
i0.ɵɵelementStart(0, "span", 33);
|
|
117
|
+
i0.ɵɵelement(1, "i");
|
|
118
|
+
i0.ɵɵelementEnd();
|
|
119
|
+
} if (rf & 2) {
|
|
120
|
+
const segment_r6 = i0.ɵɵnextContext(2).$implicit;
|
|
121
|
+
i0.ɵɵadvance();
|
|
122
|
+
i0.ɵɵclassMap(segment_r6.isExpanded ? "fa-solid fa-chevron-down" : "fa-solid fa-chevron-right");
|
|
123
|
+
} }
|
|
124
|
+
function TimelineComponent_ng_container_4_ng_container_1_div_1_ng_template_3_Template(rf, ctx) { if (rf & 1) {
|
|
125
|
+
i0.ɵɵtemplate(0, TimelineComponent_ng_container_4_ng_container_1_div_1_ng_template_3_span_0_Template, 2, 2, "span", 29);
|
|
126
|
+
i0.ɵɵelementStart(1, "span", 30);
|
|
127
|
+
i0.ɵɵtext(2);
|
|
128
|
+
i0.ɵɵelementEnd();
|
|
129
|
+
i0.ɵɵelementStart(3, "span", 31);
|
|
130
|
+
i0.ɵɵtext(4);
|
|
131
|
+
i0.ɵɵelementEnd();
|
|
132
|
+
i0.ɵɵelement(5, "span", 32);
|
|
133
|
+
} if (rf & 2) {
|
|
134
|
+
const segment_r6 = i0.ɵɵnextContext().$implicit;
|
|
135
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
136
|
+
i0.ɵɵproperty("ngIf", ctx_r1.segmentsCollapsible);
|
|
137
|
+
i0.ɵɵadvance(2);
|
|
138
|
+
i0.ɵɵtextInterpolate(segment_r6.label);
|
|
139
|
+
i0.ɵɵadvance(2);
|
|
140
|
+
i0.ɵɵtextInterpolate2("(", segment_r6.eventCount, " ", segment_r6.eventCount === 1 ? "event" : "events", ")");
|
|
141
|
+
} }
|
|
142
|
+
function TimelineComponent_ng_container_4_ng_container_1_div_1_ng_container_7_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
143
|
+
i0.ɵɵelementContainer(0);
|
|
144
|
+
} }
|
|
145
|
+
function TimelineComponent_ng_container_4_ng_container_1_div_1_ng_container_7_Template(rf, ctx) { if (rf & 1) {
|
|
146
|
+
i0.ɵɵelementContainerStart(0);
|
|
147
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_4_ng_container_1_div_1_ng_container_7_ng_container_1_Template, 1, 0, "ng-container", 28);
|
|
148
|
+
i0.ɵɵelementContainerEnd();
|
|
149
|
+
} if (rf & 2) {
|
|
150
|
+
const event_r7 = ctx.$implicit;
|
|
151
|
+
const eventIndex_r8 = ctx.index;
|
|
152
|
+
const isOdd_r9 = ctx.odd;
|
|
153
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
154
|
+
const eventCard_r10 = i0.ɵɵreference(8);
|
|
155
|
+
i0.ɵɵadvance();
|
|
156
|
+
i0.ɵɵproperty("ngTemplateOutlet", eventCard_r10)("ngTemplateOutletContext", i0.ɵɵpureFunction4(2, _c9, event_r7, eventIndex_r8, isOdd_r9, ctx_r1.getGlobalIndex(event_r7)));
|
|
157
|
+
} }
|
|
158
|
+
function TimelineComponent_ng_container_4_ng_container_1_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
159
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
160
|
+
i0.ɵɵelementStart(0, "div", 23)(1, "div", 24);
|
|
161
|
+
i0.ɵɵlistener("click", function TimelineComponent_ng_container_4_ng_container_1_div_1_Template_div_click_1_listener() { const segment_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onSegmentClick(segment_r6)); });
|
|
162
|
+
i0.ɵɵtemplate(2, TimelineComponent_ng_container_4_ng_container_1_div_1_ng_container_2_Template, 2, 4, "ng-container", 14)(3, TimelineComponent_ng_container_4_ng_container_1_div_1_ng_template_3_Template, 6, 4, "ng-template", null, 4, i0.ɵɵtemplateRefExtractor);
|
|
163
|
+
i0.ɵɵelementEnd();
|
|
164
|
+
i0.ɵɵelementStart(5, "div", 25)(6, "div", 26);
|
|
165
|
+
i0.ɵɵtemplate(7, TimelineComponent_ng_container_4_ng_container_1_div_1_ng_container_7_Template, 2, 7, "ng-container", 27);
|
|
166
|
+
i0.ɵɵelementEnd()()();
|
|
167
|
+
} if (rf & 2) {
|
|
168
|
+
const segment_r6 = ctx.$implicit;
|
|
169
|
+
const segmentIndex_r11 = ctx.index;
|
|
170
|
+
const defaultSegmentHeader_r12 = i0.ɵɵreference(4);
|
|
171
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
172
|
+
i0.ɵɵclassProp("mj-timeline__segment--collapsed", !segment_r6.isExpanded);
|
|
173
|
+
i0.ɵɵattribute("data-segment-label", segment_r6.label);
|
|
174
|
+
i0.ɵɵadvance();
|
|
175
|
+
i0.ɵɵclassProp("mj-timeline__segment-header--clickable", ctx_r1.segmentsCollapsible);
|
|
176
|
+
i0.ɵɵattribute("aria-expanded", segment_r6.isExpanded)("aria-controls", "segment-content-" + segmentIndex_r11);
|
|
177
|
+
i0.ɵɵadvance();
|
|
178
|
+
i0.ɵɵproperty("ngIf", ctx_r1.segmentHeaderTemplate)("ngIfElse", defaultSegmentHeader_r12);
|
|
179
|
+
i0.ɵɵadvance(3);
|
|
180
|
+
i0.ɵɵclassProp("mj-timeline__segment-content--hidden", !segment_r6.isExpanded);
|
|
181
|
+
i0.ɵɵproperty("id", "segment-content-" + segmentIndex_r11);
|
|
182
|
+
i0.ɵɵadvance(2);
|
|
183
|
+
i0.ɵɵproperty("ngForOf", segment_r6.events)("ngForTrackBy", ctx_r1.trackByEventId);
|
|
184
|
+
} }
|
|
185
|
+
function TimelineComponent_ng_container_4_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
186
|
+
i0.ɵɵelementContainerStart(0);
|
|
187
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_4_ng_container_1_div_1_Template, 8, 14, "div", 22);
|
|
188
|
+
i0.ɵɵelementContainerEnd();
|
|
189
|
+
} if (rf & 2) {
|
|
190
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
191
|
+
i0.ɵɵadvance();
|
|
192
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.segments)("ngForTrackBy", ctx_r1.trackBySegmentLabel);
|
|
193
|
+
} }
|
|
194
|
+
function TimelineComponent_ng_container_4_ng_container_2_ng_container_2_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
195
|
+
i0.ɵɵelementContainer(0);
|
|
196
|
+
} }
|
|
197
|
+
function TimelineComponent_ng_container_4_ng_container_2_ng_container_2_Template(rf, ctx) { if (rf & 1) {
|
|
198
|
+
i0.ɵɵelementContainerStart(0);
|
|
199
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_4_ng_container_2_ng_container_2_ng_container_1_Template, 1, 0, "ng-container", 28);
|
|
200
|
+
i0.ɵɵelementContainerEnd();
|
|
201
|
+
} if (rf & 2) {
|
|
202
|
+
const event_r13 = ctx.$implicit;
|
|
203
|
+
const eventIndex_r14 = ctx.index;
|
|
204
|
+
const isOdd_r15 = ctx.odd;
|
|
205
|
+
i0.ɵɵnextContext(3);
|
|
206
|
+
const eventCard_r10 = i0.ɵɵreference(8);
|
|
207
|
+
i0.ɵɵadvance();
|
|
208
|
+
i0.ɵɵproperty("ngTemplateOutlet", eventCard_r10)("ngTemplateOutletContext", i0.ɵɵpureFunction4(2, _c9, event_r13, eventIndex_r14, isOdd_r15, eventIndex_r14));
|
|
209
|
+
} }
|
|
210
|
+
function TimelineComponent_ng_container_4_ng_container_2_Template(rf, ctx) { if (rf & 1) {
|
|
211
|
+
i0.ɵɵelementContainerStart(0);
|
|
212
|
+
i0.ɵɵelementStart(1, "div", 26);
|
|
213
|
+
i0.ɵɵtemplate(2, TimelineComponent_ng_container_4_ng_container_2_ng_container_2_Template, 2, 7, "ng-container", 27);
|
|
214
|
+
i0.ɵɵelementEnd();
|
|
215
|
+
i0.ɵɵelementContainerEnd();
|
|
216
|
+
} if (rf & 2) {
|
|
217
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
218
|
+
i0.ɵɵadvance(2);
|
|
219
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.allEvents)("ngForTrackBy", ctx_r1.trackByEventId);
|
|
220
|
+
} }
|
|
221
|
+
function TimelineComponent_ng_container_4_Template(rf, ctx) { if (rf & 1) {
|
|
222
|
+
i0.ɵɵelementContainerStart(0);
|
|
223
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_container_4_ng_container_1_Template, 2, 2, "ng-container", 11)(2, TimelineComponent_ng_container_4_ng_container_2_Template, 3, 2, "ng-container", 11);
|
|
224
|
+
i0.ɵɵelementContainerEnd();
|
|
225
|
+
} if (rf & 2) {
|
|
226
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
227
|
+
i0.ɵɵadvance();
|
|
228
|
+
i0.ɵɵproperty("ngIf", ctx_r1.segmentGrouping !== "none");
|
|
229
|
+
i0.ɵɵadvance();
|
|
230
|
+
i0.ɵɵproperty("ngIf", ctx_r1.segmentGrouping === "none");
|
|
231
|
+
} }
|
|
232
|
+
function TimelineComponent_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
233
|
+
i0.ɵɵelement(0, "div", 34);
|
|
234
|
+
} }
|
|
235
|
+
function TimelineComponent_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
236
|
+
i0.ɵɵelementStart(0, "div", 35);
|
|
237
|
+
i0.ɵɵelement(1, "div", 36);
|
|
238
|
+
i0.ɵɵelementStart(2, "span");
|
|
239
|
+
i0.ɵɵtext(3);
|
|
240
|
+
i0.ɵɵelementEnd()();
|
|
241
|
+
} if (rf & 2) {
|
|
242
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
243
|
+
i0.ɵɵadvance(3);
|
|
244
|
+
i0.ɵɵtextInterpolate(ctx_r1.virtualScroll.loadingMessage);
|
|
245
|
+
} }
|
|
246
|
+
function TimelineComponent_ng_template_7_div_4_Template(rf, ctx) { if (rf & 1) {
|
|
247
|
+
i0.ɵɵelementStart(0, "div", 43);
|
|
248
|
+
i0.ɵɵtext(1);
|
|
249
|
+
i0.ɵɵelementEnd();
|
|
250
|
+
} if (rf & 2) {
|
|
251
|
+
const event_r17 = i0.ɵɵnextContext().event;
|
|
252
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
253
|
+
i0.ɵɵadvance();
|
|
254
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.formatDate(event_r17.date), " ");
|
|
255
|
+
} }
|
|
256
|
+
function TimelineComponent_ng_template_7_ng_container_6_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
257
|
+
i0.ɵɵelementContainer(0);
|
|
258
|
+
} }
|
|
259
|
+
function TimelineComponent_ng_template_7_ng_container_6_Template(rf, ctx) { if (rf & 1) {
|
|
260
|
+
i0.ɵɵelementContainerStart(0);
|
|
261
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_container_6_ng_container_1_Template, 1, 0, "ng-container", 28);
|
|
262
|
+
i0.ɵɵelementContainerEnd();
|
|
263
|
+
} if (rf & 2) {
|
|
264
|
+
const event_r17 = i0.ɵɵnextContext().event;
|
|
265
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
266
|
+
i0.ɵɵadvance();
|
|
267
|
+
i0.ɵɵproperty("ngTemplateOutlet", ctx_r1.cardTemplate)("ngTemplateOutletContext", i0.ɵɵpureFunction2(2, _c10, event_r17, ctx_r1.groups[event_r17.groupIndex]));
|
|
268
|
+
} }
|
|
269
|
+
function TimelineComponent_ng_template_7_ng_template_7_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
270
|
+
i0.ɵɵelementStart(0, "div", 50);
|
|
271
|
+
i0.ɵɵelement(1, "img", 51);
|
|
272
|
+
i0.ɵɵelementEnd();
|
|
273
|
+
} if (rf & 2) {
|
|
274
|
+
const event_r17 = i0.ɵɵnextContext(2).event;
|
|
275
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
276
|
+
i0.ɵɵclassProp("mj-timeline__card-image--small", ctx_r1.getEffectiveCardConfig(event_r17).imageSize === "small")("mj-timeline__card-image--medium", ctx_r1.getEffectiveCardConfig(event_r17).imageSize === "medium")("mj-timeline__card-image--large", ctx_r1.getEffectiveCardConfig(event_r17).imageSize === "large");
|
|
277
|
+
i0.ɵɵadvance();
|
|
278
|
+
i0.ɵɵproperty("src", event_r17.imageUrl, i0.ɵɵsanitizeUrl)("alt", event_r17.title);
|
|
279
|
+
} }
|
|
280
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_container_3_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
281
|
+
i0.ɵɵelementContainer(0);
|
|
282
|
+
} }
|
|
283
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_container_3_Template(rf, ctx) { if (rf & 1) {
|
|
284
|
+
i0.ɵɵelementContainerStart(0);
|
|
285
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_ng_container_3_ng_container_1_Template, 1, 0, "ng-container", 28);
|
|
286
|
+
i0.ɵɵelementContainerEnd();
|
|
287
|
+
} if (rf & 2) {
|
|
288
|
+
const event_r17 = i0.ɵɵnextContext(2).event;
|
|
289
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
290
|
+
i0.ɵɵadvance();
|
|
291
|
+
i0.ɵɵproperty("ngTemplateOutlet", ctx_r1.headerTemplate)("ngTemplateOutletContext", i0.ɵɵpureFunction1(2, _c11, event_r17));
|
|
292
|
+
} }
|
|
293
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_4_span_0_Template(rf, ctx) { if (rf & 1) {
|
|
294
|
+
i0.ɵɵelementStart(0, "span", 58);
|
|
295
|
+
i0.ɵɵelement(1, "i");
|
|
296
|
+
i0.ɵɵelementEnd();
|
|
297
|
+
} if (rf & 2) {
|
|
298
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
299
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
300
|
+
i0.ɵɵstyleProp("color", ctx_r1.getColor(event_r17));
|
|
301
|
+
i0.ɵɵadvance();
|
|
302
|
+
i0.ɵɵclassMap(ctx_r1.getIcon(event_r17));
|
|
303
|
+
} }
|
|
304
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_4_span_4_Template(rf, ctx) { if (rf & 1) {
|
|
305
|
+
i0.ɵɵelementStart(0, "span", 59);
|
|
306
|
+
i0.ɵɵtext(1);
|
|
307
|
+
i0.ɵɵelementEnd();
|
|
308
|
+
} if (rf & 2) {
|
|
309
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
310
|
+
i0.ɵɵadvance();
|
|
311
|
+
i0.ɵɵtextInterpolate1(" ", event_r17.subtitle, " ");
|
|
312
|
+
} }
|
|
313
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_4_span_5_Template(rf, ctx) { if (rf & 1) {
|
|
314
|
+
i0.ɵɵelementStart(0, "span", 60);
|
|
315
|
+
i0.ɵɵtext(1);
|
|
316
|
+
i0.ɵɵelementEnd();
|
|
317
|
+
} if (rf & 2) {
|
|
318
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
319
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
320
|
+
i0.ɵɵadvance();
|
|
321
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.formatDate(event_r17.date, ctx_r1.getEffectiveCardConfig(event_r17).dateFormat), " ");
|
|
322
|
+
} }
|
|
323
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_4_button_6_Template(rf, ctx) { if (rf & 1) {
|
|
324
|
+
const _r22 = i0.ɵɵgetCurrentView();
|
|
325
|
+
i0.ɵɵelementStart(0, "button", 61);
|
|
326
|
+
i0.ɵɵlistener("click", function TimelineComponent_ng_template_7_ng_template_7_ng_template_4_button_6_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r22); const ctx_r22 = i0.ɵɵnextContext(3); const event_r17 = ctx_r22.event; const globalIndex_r19 = ctx_r22.globalIndex; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onToggleExpand(event_r17, globalIndex_r19, $event)); });
|
|
327
|
+
i0.ɵɵelement(1, "i");
|
|
328
|
+
i0.ɵɵelementEnd();
|
|
329
|
+
} if (rf & 2) {
|
|
330
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
331
|
+
i0.ɵɵattribute("aria-label", event_r17.isExpanded ? "Collapse" : "Expand");
|
|
332
|
+
i0.ɵɵadvance();
|
|
333
|
+
i0.ɵɵclassMap(event_r17.isExpanded ? "fa-solid fa-chevron-up" : "fa-solid fa-chevron-down");
|
|
334
|
+
} }
|
|
335
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_4_Template(rf, ctx) { if (rf & 1) {
|
|
336
|
+
i0.ɵɵtemplate(0, TimelineComponent_ng_template_7_ng_template_7_ng_template_4_span_0_Template, 2, 4, "span", 52);
|
|
337
|
+
i0.ɵɵelementStart(1, "div", 53)(2, "h4", 54);
|
|
338
|
+
i0.ɵɵtext(3);
|
|
339
|
+
i0.ɵɵelementEnd();
|
|
340
|
+
i0.ɵɵtemplate(4, TimelineComponent_ng_template_7_ng_template_7_ng_template_4_span_4_Template, 2, 1, "span", 55)(5, TimelineComponent_ng_template_7_ng_template_7_ng_template_4_span_5_Template, 2, 1, "span", 56);
|
|
341
|
+
i0.ɵɵelementEnd();
|
|
342
|
+
i0.ɵɵtemplate(6, TimelineComponent_ng_template_7_ng_template_7_ng_template_4_button_6_Template, 2, 3, "button", 57);
|
|
343
|
+
} if (rf & 2) {
|
|
344
|
+
const event_r17 = i0.ɵɵnextContext(2).event;
|
|
345
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
346
|
+
i0.ɵɵproperty("ngIf", ctx_r1.getEffectiveCardConfig(event_r17).showIcon);
|
|
347
|
+
i0.ɵɵadvance(3);
|
|
348
|
+
i0.ɵɵtextInterpolate(event_r17.title);
|
|
349
|
+
i0.ɵɵadvance();
|
|
350
|
+
i0.ɵɵproperty("ngIf", event_r17.subtitle && ctx_r1.getEffectiveCardConfig(event_r17).showSubtitle);
|
|
351
|
+
i0.ɵɵadvance();
|
|
352
|
+
i0.ɵɵproperty("ngIf", ctx_r1.getEffectiveCardConfig(event_r17).showDate && ctx_r1.layout !== "alternating");
|
|
353
|
+
i0.ɵɵadvance();
|
|
354
|
+
i0.ɵɵproperty("ngIf", ctx_r1.getEffectiveCardConfig(event_r17).collapsible);
|
|
355
|
+
} }
|
|
356
|
+
function TimelineComponent_ng_template_7_ng_template_7_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
357
|
+
i0.ɵɵelementStart(0, "div", 62);
|
|
358
|
+
i0.ɵɵelement(1, "img", 51);
|
|
359
|
+
i0.ɵɵelementEnd();
|
|
360
|
+
} if (rf & 2) {
|
|
361
|
+
const event_r17 = i0.ɵɵnextContext(2).event;
|
|
362
|
+
i0.ɵɵadvance();
|
|
363
|
+
i0.ɵɵproperty("src", event_r17.imageUrl, i0.ɵɵsanitizeUrl)("alt", event_r17.title);
|
|
364
|
+
} }
|
|
365
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_container_8_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
366
|
+
i0.ɵɵelementContainer(0);
|
|
367
|
+
} }
|
|
368
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_container_8_Template(rf, ctx) { if (rf & 1) {
|
|
369
|
+
i0.ɵɵelementContainerStart(0);
|
|
370
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_ng_container_8_ng_container_1_Template, 1, 0, "ng-container", 28);
|
|
371
|
+
i0.ɵɵelementContainerEnd();
|
|
372
|
+
} if (rf & 2) {
|
|
373
|
+
const event_r17 = i0.ɵɵnextContext(2).event;
|
|
374
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
375
|
+
i0.ɵɵadvance();
|
|
376
|
+
i0.ɵɵproperty("ngTemplateOutlet", ctx_r1.bodyTemplate)("ngTemplateOutletContext", i0.ɵɵpureFunction1(2, _c11, event_r17));
|
|
377
|
+
} }
|
|
378
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_0_ng_container_1_i_2_Template(rf, ctx) { if (rf & 1) {
|
|
379
|
+
i0.ɵɵelement(0, "i", 72);
|
|
380
|
+
} if (rf & 2) {
|
|
381
|
+
const field_r24 = i0.ɵɵnextContext().$implicit;
|
|
382
|
+
i0.ɵɵproperty("ngClass", field_r24.icon);
|
|
383
|
+
} }
|
|
384
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_0_ng_container_1_span_3_Template(rf, ctx) { if (rf & 1) {
|
|
385
|
+
i0.ɵɵelementStart(0, "span", 73);
|
|
386
|
+
i0.ɵɵtext(1);
|
|
387
|
+
i0.ɵɵelementEnd();
|
|
388
|
+
} if (rf & 2) {
|
|
389
|
+
const field_r24 = i0.ɵɵnextContext().$implicit;
|
|
390
|
+
i0.ɵɵadvance();
|
|
391
|
+
i0.ɵɵtextInterpolate1("", field_r24.label, ":");
|
|
392
|
+
} }
|
|
393
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_0_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
394
|
+
i0.ɵɵelementContainerStart(0);
|
|
395
|
+
i0.ɵɵelementStart(1, "div", 68);
|
|
396
|
+
i0.ɵɵtemplate(2, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_0_ng_container_1_i_2_Template, 1, 1, "i", 69)(3, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_0_ng_container_1_span_3_Template, 2, 1, "span", 70);
|
|
397
|
+
i0.ɵɵelementStart(4, "span", 71);
|
|
398
|
+
i0.ɵɵtext(5);
|
|
399
|
+
i0.ɵɵelementEnd()();
|
|
400
|
+
i0.ɵɵelementContainerEnd();
|
|
401
|
+
} if (rf & 2) {
|
|
402
|
+
const field_r24 = ctx.$implicit;
|
|
403
|
+
const event_r17 = i0.ɵɵnextContext(4).event;
|
|
404
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
405
|
+
i0.ɵɵadvance();
|
|
406
|
+
i0.ɵɵproperty("ngClass", field_r24.cssClass);
|
|
407
|
+
i0.ɵɵadvance();
|
|
408
|
+
i0.ɵɵproperty("ngIf", field_r24.icon);
|
|
409
|
+
i0.ɵɵadvance();
|
|
410
|
+
i0.ɵɵproperty("ngIf", !field_r24.hideLabel && field_r24.label);
|
|
411
|
+
i0.ɵɵadvance(2);
|
|
412
|
+
i0.ɵɵtextInterpolate(ctx_r1.getFieldValue(event_r17, field_r24));
|
|
413
|
+
} }
|
|
414
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_0_Template(rf, ctx) { if (rf & 1) {
|
|
415
|
+
i0.ɵɵelementStart(0, "div", 66);
|
|
416
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_0_ng_container_1_Template, 6, 4, "ng-container", 67);
|
|
417
|
+
i0.ɵɵelementEnd();
|
|
418
|
+
} if (rf & 2) {
|
|
419
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
420
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
421
|
+
i0.ɵɵadvance();
|
|
422
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.getEffectiveCardConfig(event_r17).summaryFields);
|
|
423
|
+
} }
|
|
424
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_1_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
425
|
+
i0.ɵɵelementContainerStart(0);
|
|
426
|
+
i0.ɵɵelement(1, "div", 75);
|
|
427
|
+
i0.ɵɵelementContainerEnd();
|
|
428
|
+
} if (rf & 2) {
|
|
429
|
+
const event_r17 = i0.ɵɵnextContext(4).event;
|
|
430
|
+
i0.ɵɵadvance();
|
|
431
|
+
i0.ɵɵproperty("innerHTML", event_r17.description, i0.ɵɵsanitizeHtml);
|
|
432
|
+
} }
|
|
433
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_1_ng_template_2_Template(rf, ctx) { if (rf & 1) {
|
|
434
|
+
i0.ɵɵtext(0);
|
|
435
|
+
} if (rf & 2) {
|
|
436
|
+
const event_r17 = i0.ɵɵnextContext(4).event;
|
|
437
|
+
i0.ɵɵtextInterpolate1(" ", event_r17.description, " ");
|
|
438
|
+
} }
|
|
439
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
440
|
+
i0.ɵɵelementStart(0, "div", 74);
|
|
441
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_1_ng_container_1_Template, 2, 1, "ng-container", 14)(2, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_1_ng_template_2_Template, 1, 1, "ng-template", null, 8, i0.ɵɵtemplateRefExtractor);
|
|
442
|
+
i0.ɵɵelementEnd();
|
|
443
|
+
} if (rf & 2) {
|
|
444
|
+
let tmp_15_0;
|
|
445
|
+
const plainDescription_r25 = i0.ɵɵreference(3);
|
|
446
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
447
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
448
|
+
i0.ɵɵstyleProp("-webkit-line-clamp", ctx_r1.getEffectiveCardConfig(event_r17).descriptionMaxLines || null);
|
|
449
|
+
i0.ɵɵclassProp("mj-timeline__card-description--clamped", ((tmp_15_0 = ctx_r1.getEffectiveCardConfig(event_r17).descriptionMaxLines) !== null && tmp_15_0 !== undefined ? tmp_15_0 : 0) > 0);
|
|
450
|
+
i0.ɵɵadvance();
|
|
451
|
+
i0.ɵɵproperty("ngIf", ctx_r1.getEffectiveCardConfig(event_r17).allowHtmlDescription)("ngIfElse", plainDescription_r25);
|
|
452
|
+
} }
|
|
453
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_2_ng_container_1_i_2_Template(rf, ctx) { if (rf & 1) {
|
|
454
|
+
i0.ɵɵelement(0, "i", 72);
|
|
455
|
+
} if (rf & 2) {
|
|
456
|
+
const field_r26 = i0.ɵɵnextContext().$implicit;
|
|
457
|
+
i0.ɵɵproperty("ngClass", field_r26.icon);
|
|
458
|
+
} }
|
|
459
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_2_ng_container_1_span_3_Template(rf, ctx) { if (rf & 1) {
|
|
460
|
+
i0.ɵɵelementStart(0, "span", 73);
|
|
461
|
+
i0.ɵɵtext(1);
|
|
462
|
+
i0.ɵɵelementEnd();
|
|
463
|
+
} if (rf & 2) {
|
|
464
|
+
const field_r26 = i0.ɵɵnextContext().$implicit;
|
|
465
|
+
i0.ɵɵadvance();
|
|
466
|
+
i0.ɵɵtextInterpolate1("", field_r26.label || field_r26.fieldName, ":");
|
|
467
|
+
} }
|
|
468
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_2_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
469
|
+
i0.ɵɵelementContainerStart(0);
|
|
470
|
+
i0.ɵɵelementStart(1, "div", 68);
|
|
471
|
+
i0.ɵɵtemplate(2, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_2_ng_container_1_i_2_Template, 1, 1, "i", 69)(3, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_2_ng_container_1_span_3_Template, 2, 1, "span", 70);
|
|
472
|
+
i0.ɵɵelementStart(4, "span", 71);
|
|
473
|
+
i0.ɵɵtext(5);
|
|
474
|
+
i0.ɵɵelementEnd()();
|
|
475
|
+
i0.ɵɵelementContainerEnd();
|
|
476
|
+
} if (rf & 2) {
|
|
477
|
+
const field_r26 = ctx.$implicit;
|
|
478
|
+
const event_r17 = i0.ɵɵnextContext(4).event;
|
|
479
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
480
|
+
i0.ɵɵadvance();
|
|
481
|
+
i0.ɵɵproperty("ngClass", field_r26.cssClass);
|
|
482
|
+
i0.ɵɵadvance();
|
|
483
|
+
i0.ɵɵproperty("ngIf", field_r26.icon);
|
|
484
|
+
i0.ɵɵadvance();
|
|
485
|
+
i0.ɵɵproperty("ngIf", !field_r26.hideLabel);
|
|
486
|
+
i0.ɵɵadvance(2);
|
|
487
|
+
i0.ɵɵtextInterpolate(ctx_r1.getFieldValue(event_r17, field_r26));
|
|
488
|
+
} }
|
|
489
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
490
|
+
i0.ɵɵelementStart(0, "div", 76);
|
|
491
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_2_ng_container_1_Template, 6, 4, "ng-container", 67);
|
|
492
|
+
i0.ɵɵelementEnd();
|
|
493
|
+
} if (rf & 2) {
|
|
494
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
495
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
496
|
+
i0.ɵɵadvance();
|
|
497
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.getEffectiveCardConfig(event_r17).expandedFields);
|
|
498
|
+
} }
|
|
499
|
+
function TimelineComponent_ng_template_7_ng_template_7_ng_template_9_Template(rf, ctx) { if (rf & 1) {
|
|
500
|
+
i0.ɵɵtemplate(0, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_0_Template, 2, 1, "div", 63)(1, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_1_Template, 4, 6, "div", 64)(2, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_div_2_Template, 2, 1, "div", 65);
|
|
501
|
+
} if (rf & 2) {
|
|
502
|
+
let tmp_12_0;
|
|
503
|
+
let tmp_14_0;
|
|
504
|
+
const event_r17 = i0.ɵɵnextContext(2).event;
|
|
505
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
506
|
+
i0.ɵɵproperty("ngIf", (tmp_12_0 = ctx_r1.getEffectiveCardConfig(event_r17).summaryFields) == null ? null : tmp_12_0.length);
|
|
507
|
+
i0.ɵɵadvance();
|
|
508
|
+
i0.ɵɵproperty("ngIf", event_r17.description && event_r17.isExpanded);
|
|
509
|
+
i0.ɵɵadvance();
|
|
510
|
+
i0.ɵɵproperty("ngIf", event_r17.isExpanded && ((tmp_14_0 = ctx_r1.getEffectiveCardConfig(event_r17).expandedFields) == null ? null : tmp_14_0.length));
|
|
511
|
+
} }
|
|
512
|
+
function TimelineComponent_ng_template_7_ng_template_7_div_11_ng_container_1_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
513
|
+
i0.ɵɵelementContainer(0);
|
|
514
|
+
} }
|
|
515
|
+
function TimelineComponent_ng_template_7_ng_template_7_div_11_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
516
|
+
i0.ɵɵelementContainerStart(0);
|
|
517
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_div_11_ng_container_1_ng_container_1_Template, 1, 0, "ng-container", 28);
|
|
518
|
+
i0.ɵɵelementContainerEnd();
|
|
519
|
+
} if (rf & 2) {
|
|
520
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
521
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
522
|
+
i0.ɵɵadvance();
|
|
523
|
+
i0.ɵɵproperty("ngTemplateOutlet", ctx_r1.actionsTemplate)("ngTemplateOutletContext", i0.ɵɵpureFunction2(2, _c12, event_r17, ctx_r1.getActions(event_r17)));
|
|
524
|
+
} }
|
|
525
|
+
function TimelineComponent_ng_template_7_ng_template_7_div_11_ng_template_2_button_0_i_1_Template(rf, ctx) { if (rf & 1) {
|
|
526
|
+
i0.ɵɵelement(0, "i", 81);
|
|
527
|
+
} if (rf & 2) {
|
|
528
|
+
const action_r28 = i0.ɵɵnextContext().$implicit;
|
|
529
|
+
i0.ɵɵproperty("ngClass", action_r28.icon);
|
|
530
|
+
} }
|
|
531
|
+
function TimelineComponent_ng_template_7_ng_template_7_div_11_ng_template_2_button_0_Template(rf, ctx) { if (rf & 1) {
|
|
532
|
+
const _r27 = i0.ɵɵgetCurrentView();
|
|
533
|
+
i0.ɵɵelementStart(0, "button", 79);
|
|
534
|
+
i0.ɵɵlistener("click", function TimelineComponent_ng_template_7_ng_template_7_div_11_ng_template_2_button_0_Template_button_click_0_listener($event) { const action_r28 = i0.ɵɵrestoreView(_r27).$implicit; const ctx_r22 = i0.ɵɵnextContext(4); const event_r17 = ctx_r22.event; const globalIndex_r19 = ctx_r22.globalIndex; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onActionClick(event_r17, action_r28, globalIndex_r19, $event)); });
|
|
535
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_div_11_ng_template_2_button_0_i_1_Template, 1, 1, "i", 80);
|
|
536
|
+
i0.ɵɵelementStart(2, "span");
|
|
537
|
+
i0.ɵɵtext(3);
|
|
538
|
+
i0.ɵɵelementEnd()();
|
|
539
|
+
} if (rf & 2) {
|
|
540
|
+
const action_r28 = ctx.$implicit;
|
|
541
|
+
i0.ɵɵclassProp("mj-timeline__action--primary", action_r28.variant === "primary")("mj-timeline__action--secondary", action_r28.variant === "secondary" || !action_r28.variant)("mj-timeline__action--danger", action_r28.variant === "danger")("mj-timeline__action--link", action_r28.variant === "link");
|
|
542
|
+
i0.ɵɵproperty("ngClass", action_r28.cssClass)("disabled", action_r28.disabled)("title", action_r28.tooltip || "");
|
|
543
|
+
i0.ɵɵadvance();
|
|
544
|
+
i0.ɵɵproperty("ngIf", action_r28.icon);
|
|
545
|
+
i0.ɵɵadvance(2);
|
|
546
|
+
i0.ɵɵtextInterpolate(action_r28.label);
|
|
547
|
+
} }
|
|
548
|
+
function TimelineComponent_ng_template_7_ng_template_7_div_11_ng_template_2_Template(rf, ctx) { if (rf & 1) {
|
|
549
|
+
i0.ɵɵtemplate(0, TimelineComponent_ng_template_7_ng_template_7_div_11_ng_template_2_button_0_Template, 4, 13, "button", 78);
|
|
550
|
+
} if (rf & 2) {
|
|
551
|
+
const event_r17 = i0.ɵɵnextContext(3).event;
|
|
552
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
553
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.getActions(event_r17));
|
|
554
|
+
} }
|
|
555
|
+
function TimelineComponent_ng_template_7_ng_template_7_div_11_Template(rf, ctx) { if (rf & 1) {
|
|
556
|
+
i0.ɵɵelementStart(0, "div", 77);
|
|
557
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_div_11_ng_container_1_Template, 2, 5, "ng-container", 14)(2, TimelineComponent_ng_template_7_ng_template_7_div_11_ng_template_2_Template, 1, 1, "ng-template", null, 9, i0.ɵɵtemplateRefExtractor);
|
|
558
|
+
i0.ɵɵelementEnd();
|
|
559
|
+
} if (rf & 2) {
|
|
560
|
+
const defaultActions_r29 = i0.ɵɵreference(3);
|
|
561
|
+
const event_r17 = i0.ɵɵnextContext(2).event;
|
|
562
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
563
|
+
i0.ɵɵclassProp("mj-timeline__card-actions--hover-only", ctx_r1.getEffectiveCardConfig(event_r17).actionsOnHover);
|
|
564
|
+
i0.ɵɵadvance();
|
|
565
|
+
i0.ɵɵproperty("ngIf", ctx_r1.actionsTemplate)("ngIfElse", defaultActions_r29);
|
|
566
|
+
} }
|
|
567
|
+
function TimelineComponent_ng_template_7_ng_template_7_Template(rf, ctx) { if (rf & 1) {
|
|
568
|
+
i0.ɵɵelementStart(0, "div", 44);
|
|
569
|
+
i0.ɵɵtemplate(1, TimelineComponent_ng_template_7_ng_template_7_div_1_Template, 2, 8, "div", 45);
|
|
570
|
+
i0.ɵɵelementStart(2, "div", 46);
|
|
571
|
+
i0.ɵɵtemplate(3, TimelineComponent_ng_template_7_ng_template_7_ng_container_3_Template, 2, 4, "ng-container", 14)(4, TimelineComponent_ng_template_7_ng_template_7_ng_template_4_Template, 7, 5, "ng-template", null, 6, i0.ɵɵtemplateRefExtractor);
|
|
572
|
+
i0.ɵɵelementEnd()();
|
|
573
|
+
i0.ɵɵtemplate(6, TimelineComponent_ng_template_7_ng_template_7_div_6_Template, 2, 2, "div", 47);
|
|
574
|
+
i0.ɵɵelementStart(7, "div", 48);
|
|
575
|
+
i0.ɵɵtemplate(8, TimelineComponent_ng_template_7_ng_template_7_ng_container_8_Template, 2, 4, "ng-container", 14)(9, TimelineComponent_ng_template_7_ng_template_7_ng_template_9_Template, 3, 3, "ng-template", null, 7, i0.ɵɵtemplateRefExtractor);
|
|
576
|
+
i0.ɵɵelementEnd();
|
|
577
|
+
i0.ɵɵtemplate(11, TimelineComponent_ng_template_7_ng_template_7_div_11_Template, 4, 4, "div", 49);
|
|
578
|
+
} if (rf & 2) {
|
|
579
|
+
const defaultHeader_r30 = i0.ɵɵreference(5);
|
|
580
|
+
const defaultBody_r31 = i0.ɵɵreference(10);
|
|
581
|
+
const event_r17 = i0.ɵɵnextContext().event;
|
|
582
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
583
|
+
i0.ɵɵadvance();
|
|
584
|
+
i0.ɵɵproperty("ngIf", event_r17.imageUrl && ctx_r1.getEffectiveCardConfig(event_r17).imagePosition === "left");
|
|
585
|
+
i0.ɵɵadvance(2);
|
|
586
|
+
i0.ɵɵproperty("ngIf", ctx_r1.headerTemplate)("ngIfElse", defaultHeader_r30);
|
|
587
|
+
i0.ɵɵadvance(3);
|
|
588
|
+
i0.ɵɵproperty("ngIf", event_r17.imageUrl && ctx_r1.getEffectiveCardConfig(event_r17).imagePosition === "top");
|
|
589
|
+
i0.ɵɵadvance();
|
|
590
|
+
i0.ɵɵclassProp("mj-timeline__card-body--collapsed", !event_r17.isExpanded && ctx_r1.getEffectiveCardConfig(event_r17).collapsible);
|
|
591
|
+
i0.ɵɵadvance();
|
|
592
|
+
i0.ɵɵproperty("ngIf", ctx_r1.bodyTemplate)("ngIfElse", defaultBody_r31);
|
|
593
|
+
i0.ɵɵadvance(3);
|
|
594
|
+
i0.ɵɵproperty("ngIf", ctx_r1.getActions(event_r17).length > 0);
|
|
595
|
+
} }
|
|
596
|
+
function TimelineComponent_ng_template_7_Template(rf, ctx) { if (rf & 1) {
|
|
597
|
+
const _r16 = i0.ɵɵgetCurrentView();
|
|
598
|
+
i0.ɵɵelementStart(0, "div", 37)(1, "div", 38);
|
|
599
|
+
i0.ɵɵelement(2, "i", 39);
|
|
600
|
+
i0.ɵɵelementEnd();
|
|
601
|
+
i0.ɵɵelement(3, "div", 40);
|
|
602
|
+
i0.ɵɵtemplate(4, TimelineComponent_ng_template_7_div_4_Template, 2, 1, "div", 41);
|
|
603
|
+
i0.ɵɵelementStart(5, "div", 42);
|
|
604
|
+
i0.ɵɵlistener("click", function TimelineComponent_ng_template_7_Template_div_click_5_listener($event) { const ctx_r17 = i0.ɵɵrestoreView(_r16); const event_r17 = ctx_r17.event; const globalIndex_r19 = ctx_r17.globalIndex; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onEventClick(event_r17, globalIndex_r19, $event)); })("mouseenter", function TimelineComponent_ng_template_7_Template_div_mouseenter_5_listener($event) { const ctx_r19 = i0.ɵɵrestoreView(_r16); const event_r17 = ctx_r19.event; const globalIndex_r19 = ctx_r19.globalIndex; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onEventMouseEnter(event_r17, globalIndex_r19, $event)); })("mouseleave", function TimelineComponent_ng_template_7_Template_div_mouseleave_5_listener($event) { const ctx_r20 = i0.ɵɵrestoreView(_r16); const event_r17 = ctx_r20.event; const globalIndex_r19 = ctx_r20.globalIndex; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onEventMouseLeave(event_r17, globalIndex_r19, $event)); });
|
|
605
|
+
i0.ɵɵtemplate(6, TimelineComponent_ng_template_7_ng_container_6_Template, 2, 5, "ng-container", 14)(7, TimelineComponent_ng_template_7_ng_template_7_Template, 12, 9, "ng-template", null, 5, i0.ɵɵtemplateRefExtractor);
|
|
606
|
+
i0.ɵɵelementEnd()();
|
|
607
|
+
} if (rf & 2) {
|
|
608
|
+
const event_r17 = ctx.event;
|
|
609
|
+
const isOdd_r32 = ctx.isOdd;
|
|
610
|
+
const globalIndex_r19 = ctx.globalIndex;
|
|
611
|
+
const defaultCard_r33 = i0.ɵɵreference(8);
|
|
612
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
613
|
+
i0.ɵɵclassProp("mj-timeline__event--odd", isOdd_r32 && ctx_r1.layout === "alternating")("mj-timeline__event--even", !isOdd_r32 && ctx_r1.layout === "alternating")("mj-timeline__event--expanded", event_r17.isExpanded)("mj-timeline__event--focused", ctx_r1.isEventSelected(event_r17, globalIndex_r19));
|
|
614
|
+
i0.ɵɵattribute("data-event-id", event_r17.id)("aria-expanded", event_r17.isExpanded);
|
|
615
|
+
i0.ɵɵadvance();
|
|
616
|
+
i0.ɵɵstyleProp("background-color", ctx_r1.getColor(event_r17));
|
|
617
|
+
i0.ɵɵadvance();
|
|
618
|
+
i0.ɵɵclassMap(ctx_r1.getIcon(event_r17));
|
|
619
|
+
i0.ɵɵadvance();
|
|
620
|
+
i0.ɵɵstyleProp("background-color", ctx_r1.getColor(event_r17));
|
|
621
|
+
i0.ɵɵadvance();
|
|
622
|
+
i0.ɵɵproperty("ngIf", ctx_r1.layout === "alternating");
|
|
623
|
+
i0.ɵɵadvance();
|
|
624
|
+
i0.ɵɵstyleProp("max-width", ctx_r1.getEffectiveCardConfig(event_r17).maxWidth)("min-width", ctx_r1.getEffectiveCardConfig(event_r17).minWidth)("border-left-color", ctx_r1.getColor(event_r17));
|
|
625
|
+
i0.ɵɵproperty("ngClass", ctx_r1.getEffectiveCardConfig(event_r17).cssClass);
|
|
626
|
+
i0.ɵɵadvance();
|
|
627
|
+
i0.ɵɵproperty("ngIf", ctx_r1.cardTemplate)("ngIfElse", defaultCard_r33);
|
|
628
|
+
} }
|
|
629
|
+
// ============================================================================
|
|
630
|
+
// AUTO-ASSIGNED COLORS FOR GROUPS
|
|
631
|
+
// ============================================================================
|
|
632
|
+
const AUTO_COLORS = [
|
|
633
|
+
'#1976d2', // Blue
|
|
634
|
+
'#388e3c', // Green
|
|
635
|
+
'#f57c00', // Orange
|
|
636
|
+
'#7b1fa2', // Purple
|
|
637
|
+
'#c2185b', // Pink
|
|
638
|
+
'#00796b', // Teal
|
|
639
|
+
'#5d4037', // Brown
|
|
640
|
+
'#455a64', // Blue Grey
|
|
641
|
+
'#d32f2f', // Red
|
|
642
|
+
'#0097a7' // Cyan
|
|
643
|
+
];
|
|
644
|
+
const DEFAULT_ICONS = [
|
|
645
|
+
'fa-solid fa-circle',
|
|
646
|
+
'fa-solid fa-square',
|
|
647
|
+
'fa-solid fa-diamond',
|
|
648
|
+
'fa-solid fa-star',
|
|
649
|
+
'fa-solid fa-heart'
|
|
650
|
+
];
|
|
651
|
+
// ============================================================================
|
|
652
|
+
// TIMELINE COMPONENT
|
|
653
|
+
// ============================================================================
|
|
5
654
|
/**
|
|
655
|
+
* MJ Timeline Component - Displays chronological data in a rich, interactive timeline.
|
|
656
|
+
*
|
|
657
|
+
* The timeline component supports multiple data groups, virtual scrolling for large
|
|
658
|
+
* datasets, collapsible time segments, and a comprehensive event system that allows
|
|
659
|
+
* container components to intercept and modify behavior.
|
|
6
660
|
*
|
|
661
|
+
* @example Basic usage
|
|
662
|
+
* ```html
|
|
663
|
+
* <mj-timeline [groups]="myGroups"></mj-timeline>
|
|
664
|
+
* ```
|
|
665
|
+
*
|
|
666
|
+
* @example Full configuration
|
|
667
|
+
* ```html
|
|
668
|
+
* <mj-timeline
|
|
669
|
+
* [groups]="groups"
|
|
670
|
+
* orientation="vertical"
|
|
671
|
+
* layout="alternating"
|
|
672
|
+
* sortOrder="desc"
|
|
673
|
+
* segmentGrouping="month"
|
|
674
|
+
* [segmentsCollapsible]="true"
|
|
675
|
+
* [virtualScroll]="{ enabled: true, batchSize: 25 }"
|
|
676
|
+
* (beforeEventClick)="onBeforeClick($event)"
|
|
677
|
+
* (afterActionClick)="onAction($event)">
|
|
678
|
+
*
|
|
679
|
+
* <ng-template #emptyTemplate>
|
|
680
|
+
* <div class="custom-empty">No events found</div>
|
|
681
|
+
* </ng-template>
|
|
682
|
+
*
|
|
683
|
+
* </mj-timeline>
|
|
684
|
+
* ```
|
|
7
685
|
*/
|
|
8
|
-
export class
|
|
686
|
+
export class TimelineComponent {
|
|
687
|
+
cdr;
|
|
688
|
+
elementRef;
|
|
689
|
+
ngZone;
|
|
690
|
+
// ============================================================================
|
|
691
|
+
// INPUTS - DATA
|
|
692
|
+
// ============================================================================
|
|
9
693
|
/**
|
|
10
|
-
*
|
|
694
|
+
* Array of timeline groups to display.
|
|
695
|
+
* Each group defines a data source and display configuration.
|
|
11
696
|
*/
|
|
12
|
-
|
|
697
|
+
get groups() {
|
|
698
|
+
return this._groups;
|
|
699
|
+
}
|
|
700
|
+
set groups(value) {
|
|
701
|
+
const prevGroups = this._groups;
|
|
702
|
+
this._groups = value || [];
|
|
703
|
+
const hasGroups = this._groups.length > 0;
|
|
704
|
+
// Check if groups actually changed (different date field, label, or data)
|
|
705
|
+
const groupsChanged = this.didGroupsChange(prevGroups, this._groups);
|
|
706
|
+
if (this.allowLoad && hasGroups) {
|
|
707
|
+
if (!this._hasLoaded) {
|
|
708
|
+
// First load
|
|
709
|
+
this.refresh();
|
|
710
|
+
}
|
|
711
|
+
else if (groupsChanged) {
|
|
712
|
+
// Groups changed after initial load - force refresh
|
|
713
|
+
this.refresh(true);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
_groups = [];
|
|
13
718
|
/**
|
|
14
|
-
*
|
|
719
|
+
* Check if timeline groups have meaningfully changed
|
|
15
720
|
*/
|
|
16
|
-
|
|
721
|
+
didGroupsChange(prev, next) {
|
|
722
|
+
if (prev.length !== next.length)
|
|
723
|
+
return true;
|
|
724
|
+
for (let i = 0; i < prev.length; i++) {
|
|
725
|
+
const p = prev[i];
|
|
726
|
+
const n = next[i];
|
|
727
|
+
if (p.DateFieldName !== n.DateFieldName ||
|
|
728
|
+
p.GroupLabel !== n.GroupLabel ||
|
|
729
|
+
p.EntityObjects !== n.EntityObjects) {
|
|
730
|
+
return true;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
17
735
|
/**
|
|
18
|
-
*
|
|
736
|
+
* Controls whether data loading is allowed.
|
|
737
|
+
* Set to false to defer loading until ready.
|
|
738
|
+
* @default true
|
|
19
739
|
*/
|
|
20
|
-
|
|
740
|
+
get allowLoad() {
|
|
741
|
+
return this._allowLoad;
|
|
742
|
+
}
|
|
743
|
+
set allowLoad(value) {
|
|
744
|
+
const wasDisabled = !this._allowLoad;
|
|
745
|
+
this._allowLoad = value;
|
|
746
|
+
// When allowLoad becomes true and we have groups, trigger refresh
|
|
747
|
+
if (value && wasDisabled && this._groups.length > 0) {
|
|
748
|
+
this.refresh(this._hasLoaded);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
_allowLoad = true;
|
|
752
|
+
// ============================================================================
|
|
753
|
+
// INPUTS - LAYOUT (using setters for reactive updates)
|
|
754
|
+
// ============================================================================
|
|
21
755
|
/**
|
|
22
|
-
*
|
|
756
|
+
* Timeline orientation.
|
|
757
|
+
* - `vertical`: Events displayed top-to-bottom
|
|
758
|
+
* - `horizontal`: Events displayed left-to-right
|
|
759
|
+
* @default 'vertical'
|
|
23
760
|
*/
|
|
24
|
-
|
|
761
|
+
get orientation() {
|
|
762
|
+
return this._orientation;
|
|
763
|
+
}
|
|
764
|
+
set orientation(value) {
|
|
765
|
+
if (this._orientation !== value) {
|
|
766
|
+
this._orientation = value;
|
|
767
|
+
this.cdr.markForCheck();
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
_orientation = 'vertical';
|
|
25
771
|
/**
|
|
26
|
-
*
|
|
772
|
+
* Layout mode for vertical timeline.
|
|
773
|
+
* - `single`: All cards on one side
|
|
774
|
+
* - `alternating`: Cards alternate sides
|
|
775
|
+
* @default 'single'
|
|
27
776
|
*/
|
|
28
|
-
|
|
777
|
+
get layout() {
|
|
778
|
+
return this._layout;
|
|
779
|
+
}
|
|
780
|
+
set layout(value) {
|
|
781
|
+
if (this._layout !== value) {
|
|
782
|
+
this._layout = value;
|
|
783
|
+
this.cdr.markForCheck();
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
_layout = 'single';
|
|
29
787
|
/**
|
|
30
|
-
*
|
|
788
|
+
* Sort order for events.
|
|
789
|
+
* - `desc`: Newest first
|
|
790
|
+
* - `asc`: Oldest first
|
|
791
|
+
* @default 'desc'
|
|
31
792
|
*/
|
|
32
|
-
|
|
793
|
+
get sortOrder() {
|
|
794
|
+
return this._sortOrder;
|
|
795
|
+
}
|
|
796
|
+
set sortOrder(value) {
|
|
797
|
+
if (this._sortOrder !== value) {
|
|
798
|
+
this._sortOrder = value;
|
|
799
|
+
// Re-process events when sort order changes - force refresh since data already loaded
|
|
800
|
+
if (this._initialized) {
|
|
801
|
+
this.refresh(true);
|
|
802
|
+
}
|
|
803
|
+
this.cdr.markForCheck();
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
_sortOrder = 'desc';
|
|
33
807
|
/**
|
|
34
|
-
*
|
|
808
|
+
* How to group events into time segments.
|
|
809
|
+
* @default 'month'
|
|
35
810
|
*/
|
|
36
|
-
|
|
811
|
+
get segmentGrouping() {
|
|
812
|
+
return this._segmentGrouping;
|
|
813
|
+
}
|
|
814
|
+
set segmentGrouping(value) {
|
|
815
|
+
if (this._segmentGrouping !== value) {
|
|
816
|
+
this._segmentGrouping = value;
|
|
817
|
+
// Re-segment events when grouping changes - force refresh since data already loaded
|
|
818
|
+
if (this._initialized) {
|
|
819
|
+
this.refresh(true);
|
|
820
|
+
}
|
|
821
|
+
this.cdr.markForCheck();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
_segmentGrouping = 'month';
|
|
825
|
+
// ============================================================================
|
|
826
|
+
// INPUTS - CARD DEFAULTS
|
|
827
|
+
// ============================================================================
|
|
828
|
+
/**
|
|
829
|
+
* Default card configuration applied to all groups.
|
|
830
|
+
* Individual groups can override these settings.
|
|
831
|
+
*/
|
|
832
|
+
defaultCardConfig = { ...DEFAULT_CARD_CONFIG };
|
|
833
|
+
// ============================================================================
|
|
834
|
+
// INPUTS - VIRTUAL SCROLLING
|
|
835
|
+
// ============================================================================
|
|
836
|
+
/**
|
|
837
|
+
* Virtual scrolling configuration.
|
|
838
|
+
*/
|
|
839
|
+
virtualScroll = { ...DEFAULT_VIRTUAL_SCROLL_CONFIG };
|
|
840
|
+
// ============================================================================
|
|
841
|
+
// INPUTS - SEGMENTS
|
|
842
|
+
// ============================================================================
|
|
843
|
+
/**
|
|
844
|
+
* Whether time segments can be collapsed.
|
|
845
|
+
* @default true
|
|
846
|
+
*/
|
|
847
|
+
segmentsCollapsible = true;
|
|
848
|
+
/**
|
|
849
|
+
* Whether segments start expanded.
|
|
850
|
+
* @default true
|
|
851
|
+
*/
|
|
852
|
+
segmentsDefaultExpanded = true;
|
|
853
|
+
// ============================================================================
|
|
854
|
+
// INPUTS - EMPTY & LOADING STATES
|
|
855
|
+
// ============================================================================
|
|
37
856
|
/**
|
|
38
|
-
*
|
|
857
|
+
* Message shown when no events exist.
|
|
858
|
+
* @default 'No events to display'
|
|
39
859
|
*/
|
|
40
|
-
|
|
860
|
+
emptyMessage = 'No events to display';
|
|
41
861
|
/**
|
|
42
|
-
*
|
|
862
|
+
* Icon shown with empty message.
|
|
863
|
+
* @default 'fa-regular fa-calendar-xmark'
|
|
43
864
|
*/
|
|
44
|
-
|
|
865
|
+
emptyIcon = 'fa-regular fa-calendar-xmark';
|
|
45
866
|
/**
|
|
46
|
-
*
|
|
867
|
+
* Message shown while loading.
|
|
868
|
+
* @default 'Loading timeline...'
|
|
47
869
|
*/
|
|
48
|
-
|
|
870
|
+
loadingMessage = 'Loading timeline...';
|
|
871
|
+
// ============================================================================
|
|
872
|
+
// INPUTS - ACCESSIBILITY
|
|
873
|
+
// ============================================================================
|
|
49
874
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
875
|
+
* ARIA label for the timeline container.
|
|
876
|
+
* @default 'Timeline'
|
|
52
877
|
*/
|
|
53
|
-
|
|
878
|
+
ariaLabel = 'Timeline';
|
|
54
879
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
880
|
+
* Enable keyboard navigation.
|
|
881
|
+
* @default true
|
|
57
882
|
*/
|
|
58
|
-
|
|
883
|
+
enableKeyboardNavigation = true;
|
|
59
884
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* @param params
|
|
63
|
-
* @returns
|
|
885
|
+
* ID of the currently selected event.
|
|
886
|
+
* When set, the corresponding event will be highlighted with the focused style.
|
|
64
887
|
*/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
888
|
+
get selectedEventId() {
|
|
889
|
+
return this._selectedEventId;
|
|
890
|
+
}
|
|
891
|
+
set selectedEventId(value) {
|
|
892
|
+
const changed = this._selectedEventId !== value;
|
|
893
|
+
this._selectedEventId = value;
|
|
894
|
+
if (changed) {
|
|
895
|
+
this.cdr.markForCheck();
|
|
73
896
|
}
|
|
74
|
-
return group;
|
|
75
897
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
898
|
+
_selectedEventId = null;
|
|
899
|
+
// ============================================================================
|
|
900
|
+
// OUTPUTS - BEFORE EVENTS (with cancel support)
|
|
901
|
+
// ============================================================================
|
|
902
|
+
/** Emitted before an event card is clicked. Set `cancel = true` to prevent. */
|
|
903
|
+
beforeEventClick = new EventEmitter();
|
|
904
|
+
/** Emitted before an event card expands. Set `cancel = true` to prevent. */
|
|
905
|
+
beforeEventExpand = new EventEmitter();
|
|
906
|
+
/** Emitted before an event card collapses. Set `cancel = true` to prevent. */
|
|
907
|
+
beforeEventCollapse = new EventEmitter();
|
|
908
|
+
/** Emitted before hover state changes. Set `cancel = true` to prevent. */
|
|
909
|
+
beforeEventHover = new EventEmitter();
|
|
910
|
+
/** Emitted before an action button is clicked. Set `cancel = true` to prevent. */
|
|
911
|
+
beforeActionClick = new EventEmitter();
|
|
912
|
+
/** Emitted before a time segment expands. Set `cancel = true` to prevent. */
|
|
913
|
+
beforeSegmentExpand = new EventEmitter();
|
|
914
|
+
/** Emitted before a time segment collapses. Set `cancel = true` to prevent. */
|
|
915
|
+
beforeSegmentCollapse = new EventEmitter();
|
|
916
|
+
/** Emitted before data loading begins. Set `cancel = true` to prevent. */
|
|
917
|
+
beforeLoad = new EventEmitter();
|
|
918
|
+
// ============================================================================
|
|
919
|
+
// OUTPUTS - AFTER EVENTS
|
|
920
|
+
// ============================================================================
|
|
921
|
+
/** Emitted after an event card is clicked. */
|
|
922
|
+
afterEventClick = new EventEmitter();
|
|
923
|
+
/** Emitted after an event card expands. */
|
|
924
|
+
afterEventExpand = new EventEmitter();
|
|
925
|
+
/** Emitted after an event card collapses. */
|
|
926
|
+
afterEventCollapse = new EventEmitter();
|
|
927
|
+
/** Emitted after hover state changes. */
|
|
928
|
+
afterEventHover = new EventEmitter();
|
|
929
|
+
/** Emitted after an action button is clicked. */
|
|
930
|
+
afterActionClick = new EventEmitter();
|
|
931
|
+
/** Emitted after a time segment expands. */
|
|
932
|
+
afterSegmentExpand = new EventEmitter();
|
|
933
|
+
/** Emitted after a time segment collapses. */
|
|
934
|
+
afterSegmentCollapse = new EventEmitter();
|
|
935
|
+
/** Emitted after data loading completes. */
|
|
936
|
+
afterLoad = new EventEmitter();
|
|
937
|
+
// ============================================================================
|
|
938
|
+
// CONTENT CHILDREN - OPTIONAL TEMPLATES
|
|
939
|
+
// ============================================================================
|
|
940
|
+
/** Custom template for entire card. Context: { event, group } */
|
|
941
|
+
cardTemplate;
|
|
942
|
+
/** Custom template for card header. Context: { event } */
|
|
943
|
+
headerTemplate;
|
|
944
|
+
/** Custom template for card body. Context: { event } */
|
|
945
|
+
bodyTemplate;
|
|
946
|
+
/** Custom template for card actions. Context: { event, actions } */
|
|
947
|
+
actionsTemplate;
|
|
948
|
+
/** Custom template for segment header. Context: { segment } */
|
|
949
|
+
segmentHeaderTemplate;
|
|
950
|
+
/** Custom template for empty state. */
|
|
951
|
+
emptyTemplate;
|
|
952
|
+
/** Custom template for loading state. */
|
|
953
|
+
loadingTemplate;
|
|
954
|
+
// ============================================================================
|
|
955
|
+
// VIEW CHILDREN
|
|
956
|
+
// ============================================================================
|
|
957
|
+
scrollContainer;
|
|
958
|
+
// ============================================================================
|
|
959
|
+
// PUBLIC PROPERTIES
|
|
960
|
+
// ============================================================================
|
|
961
|
+
/** Current time segments with events. */
|
|
962
|
+
segments = [];
|
|
963
|
+
/** All flattened events (for non-segmented display). */
|
|
964
|
+
allEvents = [];
|
|
965
|
+
/** Virtual scroll state. */
|
|
966
|
+
scrollState = { ...DEFAULT_VIRTUAL_SCROLL_STATE };
|
|
967
|
+
/** Whether initial load is complete. */
|
|
968
|
+
isInitialized = false;
|
|
969
|
+
/** Whether currently loading data. */
|
|
970
|
+
isLoading = false;
|
|
971
|
+
/** Index of currently focused event (for keyboard navigation). */
|
|
972
|
+
focusedEventIndex = -1;
|
|
973
|
+
// ============================================================================
|
|
974
|
+
// PRIVATE PROPERTIES
|
|
975
|
+
// ============================================================================
|
|
976
|
+
_initialized = false;
|
|
977
|
+
_hasLoaded = false;
|
|
978
|
+
_destroy$ = new Subject();
|
|
979
|
+
_scroll$ = new Subject();
|
|
980
|
+
_intersectionObserver;
|
|
981
|
+
// ============================================================================
|
|
982
|
+
// CONSTRUCTOR
|
|
983
|
+
// ============================================================================
|
|
984
|
+
constructor(cdr, elementRef, ngZone) {
|
|
985
|
+
this.cdr = cdr;
|
|
986
|
+
this.elementRef = elementRef;
|
|
987
|
+
this.ngZone = ngZone;
|
|
988
|
+
}
|
|
989
|
+
// ============================================================================
|
|
990
|
+
// LIFECYCLE HOOKS
|
|
991
|
+
// ============================================================================
|
|
992
|
+
ngOnInit() {
|
|
993
|
+
this._initialized = true;
|
|
994
|
+
// Set up scroll listener for virtual scrolling
|
|
995
|
+
this._scroll$
|
|
996
|
+
.pipe(debounceTime(100), takeUntil(this._destroy$))
|
|
997
|
+
.subscribe(() => this.onScrollCheck());
|
|
998
|
+
}
|
|
999
|
+
ngAfterViewInit() {
|
|
1000
|
+
if (this.allowLoad && !this._hasLoaded) {
|
|
1001
|
+
this.refresh();
|
|
1002
|
+
}
|
|
1003
|
+
// Set up intersection observer for virtual scroll trigger
|
|
1004
|
+
this.setupIntersectionObserver();
|
|
1005
|
+
}
|
|
1006
|
+
ngOnDestroy() {
|
|
1007
|
+
this._destroy$.next();
|
|
1008
|
+
this._destroy$.complete();
|
|
1009
|
+
if (this._intersectionObserver) {
|
|
1010
|
+
this._intersectionObserver.disconnect();
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
// ============================================================================
|
|
1014
|
+
// PUBLIC METHODS
|
|
1015
|
+
// ============================================================================
|
|
83
1016
|
/**
|
|
84
|
-
*
|
|
1017
|
+
* Refreshes all data from the configured groups.
|
|
1018
|
+
* Clears existing data and reloads from sources.
|
|
85
1019
|
*/
|
|
86
|
-
|
|
87
|
-
|
|
1020
|
+
async refresh(force = false) {
|
|
1021
|
+
// Prevent concurrent refresh calls - if already refreshing, exit immediately
|
|
1022
|
+
if (this.isLoading || (this._hasLoaded && !force)) {
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
const startTime = Date.now();
|
|
1026
|
+
// Emit before event
|
|
1027
|
+
const beforeArgs = {
|
|
1028
|
+
cancel: false,
|
|
1029
|
+
groups: this._groups,
|
|
1030
|
+
isIncremental: false
|
|
1031
|
+
};
|
|
1032
|
+
this.beforeLoad.emit(beforeArgs);
|
|
1033
|
+
if (beforeArgs.cancel) {
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
this.isLoading = true;
|
|
1037
|
+
this.cdr.markForCheck();
|
|
1038
|
+
try {
|
|
1039
|
+
// Clear existing data
|
|
1040
|
+
this.allEvents = [];
|
|
1041
|
+
this.segments = [];
|
|
1042
|
+
this.scrollState = { ...DEFAULT_VIRTUAL_SCROLL_STATE };
|
|
1043
|
+
// Load data from all groups
|
|
1044
|
+
await this.loadAllGroups();
|
|
1045
|
+
// Group into segments
|
|
1046
|
+
this.buildSegments();
|
|
1047
|
+
this._hasLoaded = true;
|
|
1048
|
+
this.isInitialized = true;
|
|
1049
|
+
// Emit after event
|
|
1050
|
+
const afterArgs = {
|
|
1051
|
+
success: true,
|
|
1052
|
+
eventsLoaded: this.allEvents.length,
|
|
1053
|
+
totalEvents: this.allEvents.length,
|
|
1054
|
+
loadTimeMs: Date.now() - startTime,
|
|
1055
|
+
hasMore: this.scrollState.hasMore
|
|
1056
|
+
};
|
|
1057
|
+
this.afterLoad.emit(afterArgs);
|
|
1058
|
+
}
|
|
1059
|
+
catch (error) {
|
|
1060
|
+
console.error('Timeline: Error loading data', error);
|
|
1061
|
+
const afterArgs = {
|
|
1062
|
+
success: false,
|
|
1063
|
+
errorMessage: error instanceof Error ? error.message : 'Unknown error',
|
|
1064
|
+
eventsLoaded: 0,
|
|
1065
|
+
totalEvents: 0,
|
|
1066
|
+
loadTimeMs: Date.now() - startTime,
|
|
1067
|
+
hasMore: false
|
|
1068
|
+
};
|
|
1069
|
+
this.afterLoad.emit(afterArgs);
|
|
1070
|
+
}
|
|
1071
|
+
finally {
|
|
1072
|
+
this.isLoading = false;
|
|
1073
|
+
this.cdr.markForCheck();
|
|
1074
|
+
}
|
|
88
1075
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
1076
|
+
/**
|
|
1077
|
+
* Loads more events (for virtual scrolling).
|
|
1078
|
+
*/
|
|
1079
|
+
async loadMore() {
|
|
1080
|
+
if (!this.virtualScroll.enabled || this.scrollState.isLoading || !this.scrollState.hasMore) {
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
const startTime = Date.now();
|
|
1084
|
+
// Emit before event
|
|
1085
|
+
const beforeArgs = {
|
|
1086
|
+
cancel: false,
|
|
1087
|
+
groups: this._groups,
|
|
1088
|
+
isIncremental: true,
|
|
1089
|
+
offset: this.scrollState.loadedCount,
|
|
1090
|
+
batchSize: this.virtualScroll.batchSize
|
|
1091
|
+
};
|
|
1092
|
+
this.beforeLoad.emit(beforeArgs);
|
|
1093
|
+
if (beforeArgs.cancel) {
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
this.scrollState.isLoading = true;
|
|
1097
|
+
this.cdr.markForCheck();
|
|
1098
|
+
try {
|
|
1099
|
+
const previousCount = this.allEvents.length;
|
|
1100
|
+
// Load next batch from all groups
|
|
1101
|
+
await this.loadNextBatch();
|
|
1102
|
+
// Rebuild segments with new data
|
|
1103
|
+
this.buildSegments();
|
|
1104
|
+
const newCount = this.allEvents.length - previousCount;
|
|
1105
|
+
// Emit after event
|
|
1106
|
+
const afterArgs = {
|
|
1107
|
+
success: true,
|
|
1108
|
+
eventsLoaded: newCount,
|
|
1109
|
+
totalEvents: this.allEvents.length,
|
|
1110
|
+
loadTimeMs: Date.now() - startTime,
|
|
1111
|
+
hasMore: this.scrollState.hasMore
|
|
1112
|
+
};
|
|
1113
|
+
this.afterLoad.emit(afterArgs);
|
|
1114
|
+
}
|
|
1115
|
+
catch (error) {
|
|
1116
|
+
console.error('Timeline: Error loading more data', error);
|
|
1117
|
+
}
|
|
1118
|
+
finally {
|
|
1119
|
+
this.scrollState.isLoading = false;
|
|
1120
|
+
this.cdr.markForCheck();
|
|
1121
|
+
}
|
|
93
1122
|
}
|
|
94
|
-
_deferLoadCount = 0;
|
|
95
|
-
_allowLoad = true;
|
|
96
1123
|
/**
|
|
97
|
-
*
|
|
1124
|
+
* Expands all event cards.
|
|
98
1125
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
1126
|
+
expandAllEvents() {
|
|
1127
|
+
for (const event of this.allEvents) {
|
|
1128
|
+
if (!event.isExpanded) {
|
|
1129
|
+
this.setEventExpanded(event, true);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
this.cdr.markForCheck();
|
|
101
1133
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
1134
|
+
/**
|
|
1135
|
+
* Collapses all event cards.
|
|
1136
|
+
*/
|
|
1137
|
+
collapseAllEvents() {
|
|
1138
|
+
for (const event of this.allEvents) {
|
|
1139
|
+
if (event.isExpanded) {
|
|
1140
|
+
this.setEventExpanded(event, false);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
this.cdr.markForCheck();
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Expands all time segments.
|
|
1147
|
+
*/
|
|
1148
|
+
expandAllSegments() {
|
|
1149
|
+
for (const segment of this.segments) {
|
|
1150
|
+
if (!segment.isExpanded) {
|
|
1151
|
+
this.setSegmentExpanded(segment, true);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
this.cdr.markForCheck();
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Collapses all time segments.
|
|
1158
|
+
*/
|
|
1159
|
+
collapseAllSegments() {
|
|
1160
|
+
for (const segment of this.segments) {
|
|
1161
|
+
if (segment.isExpanded) {
|
|
1162
|
+
this.setSegmentExpanded(segment, false);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
this.cdr.markForCheck();
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Expands a specific event by ID.
|
|
1169
|
+
*/
|
|
1170
|
+
expandEvent(eventId) {
|
|
1171
|
+
const event = this.getEvent(eventId);
|
|
1172
|
+
if (event && !event.isExpanded) {
|
|
1173
|
+
this.setEventExpanded(event, true);
|
|
1174
|
+
this.cdr.markForCheck();
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Collapses a specific event by ID.
|
|
1179
|
+
*/
|
|
1180
|
+
collapseEvent(eventId) {
|
|
1181
|
+
const event = this.getEvent(eventId);
|
|
1182
|
+
if (event && event.isExpanded) {
|
|
1183
|
+
this.setEventExpanded(event, false);
|
|
1184
|
+
this.cdr.markForCheck();
|
|
107
1185
|
}
|
|
108
1186
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
1187
|
+
/**
|
|
1188
|
+
* Scrolls to a specific event.
|
|
1189
|
+
*/
|
|
1190
|
+
scrollToEvent(eventId, behavior = 'smooth') {
|
|
1191
|
+
const element = this.elementRef.nativeElement.querySelector(`[data-event-id="${eventId}"]`);
|
|
1192
|
+
if (element) {
|
|
1193
|
+
element.scrollIntoView({ behavior, block: 'center' });
|
|
1194
|
+
}
|
|
117
1195
|
}
|
|
118
1196
|
/**
|
|
119
|
-
*
|
|
1197
|
+
* Scrolls to a specific date.
|
|
120
1198
|
*/
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
1199
|
+
scrollToDate(date, behavior = 'smooth') {
|
|
1200
|
+
// Find the segment containing this date
|
|
1201
|
+
const segment = this.segments.find(s => date >= s.startDate && date < s.endDate);
|
|
1202
|
+
if (segment) {
|
|
1203
|
+
const element = this.elementRef.nativeElement.querySelector(`[data-segment-label="${segment.label}"]`);
|
|
1204
|
+
if (element) {
|
|
1205
|
+
element.scrollIntoView({ behavior, block: 'start' });
|
|
126
1206
|
}
|
|
127
1207
|
}
|
|
128
1208
|
}
|
|
129
1209
|
/**
|
|
130
|
-
*
|
|
131
|
-
|
|
1210
|
+
* Gets an event by ID.
|
|
1211
|
+
*/
|
|
1212
|
+
getEvent(eventId) {
|
|
1213
|
+
return this.allEvents.find(e => e.id === eventId);
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Gets all events (flattened).
|
|
1217
|
+
*/
|
|
1218
|
+
getAllEvents() {
|
|
1219
|
+
return [...this.allEvents];
|
|
1220
|
+
}
|
|
1221
|
+
// ============================================================================
|
|
1222
|
+
// EVENT HANDLERS (Template bindings)
|
|
1223
|
+
// ============================================================================
|
|
1224
|
+
/**
|
|
1225
|
+
* Handles click on an event card.
|
|
1226
|
+
*/
|
|
1227
|
+
onEventClick(event, index, domEvent) {
|
|
1228
|
+
const group = this._groups[event.groupIndex];
|
|
1229
|
+
// Emit before event
|
|
1230
|
+
const beforeArgs = {
|
|
1231
|
+
cancel: false,
|
|
1232
|
+
event,
|
|
1233
|
+
group,
|
|
1234
|
+
index,
|
|
1235
|
+
domEvent
|
|
1236
|
+
};
|
|
1237
|
+
this.beforeEventClick.emit(beforeArgs);
|
|
1238
|
+
if (beforeArgs.cancel) {
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
// Default behavior: toggle expand/collapse if collapsible
|
|
1242
|
+
const cardConfig = this.getEffectiveCardConfig(event);
|
|
1243
|
+
if (cardConfig.collapsible) {
|
|
1244
|
+
this.toggleEventExpanded(event, index, domEvent);
|
|
1245
|
+
}
|
|
1246
|
+
// Emit after event
|
|
1247
|
+
const afterArgs = {
|
|
1248
|
+
success: true,
|
|
1249
|
+
event,
|
|
1250
|
+
group,
|
|
1251
|
+
index,
|
|
1252
|
+
domEvent
|
|
1253
|
+
};
|
|
1254
|
+
this.afterEventClick.emit(afterArgs);
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Handles expand/collapse toggle on an event.
|
|
1258
|
+
*/
|
|
1259
|
+
onToggleExpand(event, index, domEvent) {
|
|
1260
|
+
domEvent.stopPropagation();
|
|
1261
|
+
this.toggleEventExpanded(event, index, domEvent);
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Handles mouse enter on an event card.
|
|
1265
|
+
*/
|
|
1266
|
+
onEventMouseEnter(event, index, domEvent) {
|
|
1267
|
+
const group = this._groups[event.groupIndex];
|
|
1268
|
+
const beforeArgs = {
|
|
1269
|
+
cancel: false,
|
|
1270
|
+
event,
|
|
1271
|
+
group,
|
|
1272
|
+
index,
|
|
1273
|
+
domEvent,
|
|
1274
|
+
hoverState: 'enter'
|
|
1275
|
+
};
|
|
1276
|
+
this.beforeEventHover.emit(beforeArgs);
|
|
1277
|
+
if (!beforeArgs.cancel) {
|
|
1278
|
+
const afterArgs = {
|
|
1279
|
+
success: true,
|
|
1280
|
+
event,
|
|
1281
|
+
group,
|
|
1282
|
+
index,
|
|
1283
|
+
domEvent,
|
|
1284
|
+
hoverState: 'enter'
|
|
1285
|
+
};
|
|
1286
|
+
this.afterEventHover.emit(afterArgs);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Handles mouse leave on an event card.
|
|
1291
|
+
*/
|
|
1292
|
+
onEventMouseLeave(event, index, domEvent) {
|
|
1293
|
+
const group = this._groups[event.groupIndex];
|
|
1294
|
+
const beforeArgs = {
|
|
1295
|
+
cancel: false,
|
|
1296
|
+
event,
|
|
1297
|
+
group,
|
|
1298
|
+
index,
|
|
1299
|
+
domEvent,
|
|
1300
|
+
hoverState: 'leave'
|
|
1301
|
+
};
|
|
1302
|
+
this.beforeEventHover.emit(beforeArgs);
|
|
1303
|
+
if (!beforeArgs.cancel) {
|
|
1304
|
+
const afterArgs = {
|
|
1305
|
+
success: true,
|
|
1306
|
+
event,
|
|
1307
|
+
group,
|
|
1308
|
+
index,
|
|
1309
|
+
domEvent,
|
|
1310
|
+
hoverState: 'leave'
|
|
1311
|
+
};
|
|
1312
|
+
this.afterEventHover.emit(afterArgs);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Handles action button click.
|
|
1317
|
+
*/
|
|
1318
|
+
onActionClick(event, action, index, domEvent) {
|
|
1319
|
+
domEvent.stopPropagation();
|
|
1320
|
+
if (action.disabled) {
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
const group = this._groups[event.groupIndex];
|
|
1324
|
+
const beforeArgs = {
|
|
1325
|
+
cancel: false,
|
|
1326
|
+
event,
|
|
1327
|
+
group,
|
|
1328
|
+
index,
|
|
1329
|
+
domEvent,
|
|
1330
|
+
action
|
|
1331
|
+
};
|
|
1332
|
+
this.beforeActionClick.emit(beforeArgs);
|
|
1333
|
+
if (!beforeArgs.cancel) {
|
|
1334
|
+
const afterArgs = {
|
|
1335
|
+
success: true,
|
|
1336
|
+
event,
|
|
1337
|
+
group,
|
|
1338
|
+
index,
|
|
1339
|
+
domEvent,
|
|
1340
|
+
action
|
|
1341
|
+
};
|
|
1342
|
+
this.afterActionClick.emit(afterArgs);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Handles segment header click.
|
|
1347
|
+
*/
|
|
1348
|
+
onSegmentClick(segment) {
|
|
1349
|
+
if (!this.segmentsCollapsible) {
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
if (segment.isExpanded) {
|
|
1353
|
+
this.collapseSegment(segment);
|
|
1354
|
+
}
|
|
1355
|
+
else {
|
|
1356
|
+
this.expandSegment(segment);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Handles scroll events for virtual scrolling.
|
|
132
1361
|
*/
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
1362
|
+
onScroll(event) {
|
|
1363
|
+
this._scroll$.next(event);
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Handles keyboard navigation.
|
|
1367
|
+
*/
|
|
1368
|
+
onKeyDown(event) {
|
|
1369
|
+
if (!this.enableKeyboardNavigation) {
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
switch (event.key) {
|
|
1373
|
+
case 'ArrowDown':
|
|
1374
|
+
case 'ArrowRight':
|
|
1375
|
+
event.preventDefault();
|
|
1376
|
+
this.focusNextEvent();
|
|
142
1377
|
break;
|
|
143
|
-
case '
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
1378
|
+
case 'ArrowUp':
|
|
1379
|
+
case 'ArrowLeft':
|
|
1380
|
+
event.preventDefault();
|
|
1381
|
+
this.focusPreviousEvent();
|
|
1382
|
+
break;
|
|
1383
|
+
case 'Enter':
|
|
1384
|
+
case ' ':
|
|
1385
|
+
event.preventDefault();
|
|
1386
|
+
this.activateFocusedEvent();
|
|
1387
|
+
break;
|
|
1388
|
+
case 'Escape':
|
|
1389
|
+
event.preventDefault();
|
|
1390
|
+
this.collapseFocusedEvent();
|
|
1391
|
+
break;
|
|
1392
|
+
case 'Home':
|
|
1393
|
+
event.preventDefault();
|
|
1394
|
+
this.focusFirstEvent();
|
|
1395
|
+
break;
|
|
1396
|
+
case 'End':
|
|
1397
|
+
event.preventDefault();
|
|
1398
|
+
this.focusLastEvent();
|
|
153
1399
|
break;
|
|
154
1400
|
}
|
|
155
|
-
this.events.push(...newItems);
|
|
156
1401
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
1402
|
+
// ============================================================================
|
|
1403
|
+
// TEMPLATE HELPERS
|
|
1404
|
+
// ============================================================================
|
|
1405
|
+
/**
|
|
1406
|
+
* Gets the effective card config for an event.
|
|
1407
|
+
*/
|
|
1408
|
+
getEffectiveCardConfig(event) {
|
|
1409
|
+
const group = this._groups[event.groupIndex];
|
|
1410
|
+
return {
|
|
1411
|
+
...this.defaultCardConfig,
|
|
1412
|
+
...group?.CardConfig,
|
|
1413
|
+
...this.mapEventConfigToCardConfig(event.config)
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Gets the color for a group/event.
|
|
1418
|
+
*/
|
|
1419
|
+
getColor(event) {
|
|
1420
|
+
if (event.config?.color) {
|
|
1421
|
+
return event.config.color;
|
|
1422
|
+
}
|
|
1423
|
+
const group = this._groups[event.groupIndex];
|
|
1424
|
+
if (group?.DisplayColorMode === 'manual' && group.DisplayColor) {
|
|
1425
|
+
return group.DisplayColor;
|
|
1426
|
+
}
|
|
1427
|
+
return AUTO_COLORS[event.groupIndex % AUTO_COLORS.length];
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Gets the icon for a group/event.
|
|
1431
|
+
*/
|
|
1432
|
+
getIcon(event) {
|
|
1433
|
+
if (event.config?.icon) {
|
|
1434
|
+
return event.config.icon;
|
|
1435
|
+
}
|
|
1436
|
+
const group = this._groups[event.groupIndex];
|
|
1437
|
+
if (group?.DisplayIconMode === 'custom' && group.DisplayIcon) {
|
|
1438
|
+
return group.DisplayIcon;
|
|
1439
|
+
}
|
|
1440
|
+
return DEFAULT_ICONS[event.groupIndex % DEFAULT_ICONS.length];
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Gets the actions for an event.
|
|
1444
|
+
*/
|
|
1445
|
+
getActions(event) {
|
|
1446
|
+
if (event.config?.actions) {
|
|
1447
|
+
return event.config.actions;
|
|
1448
|
+
}
|
|
1449
|
+
const cardConfig = this.getEffectiveCardConfig(event);
|
|
1450
|
+
return cardConfig.actions || [];
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Formats a date for display.
|
|
1454
|
+
*/
|
|
1455
|
+
formatDate(date, format) {
|
|
1456
|
+
const fmt = format || this.defaultCardConfig.dateFormat || 'MMM d, yyyy';
|
|
1457
|
+
return this.formatDateInternal(date, fmt);
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Gets the value of a display field from an event.
|
|
1461
|
+
*/
|
|
1462
|
+
getFieldValue(event, field) {
|
|
1463
|
+
const value = getFieldValue(event.entity, field.fieldName);
|
|
1464
|
+
if (field.formatter) {
|
|
1465
|
+
return field.formatter(value);
|
|
1466
|
+
}
|
|
1467
|
+
if (value == null) {
|
|
1468
|
+
return '';
|
|
1469
|
+
}
|
|
1470
|
+
if (field.format && value instanceof Date) {
|
|
1471
|
+
return this.formatDateInternal(value, field.format);
|
|
1472
|
+
}
|
|
1473
|
+
return String(value);
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Track by function for ngFor.
|
|
1477
|
+
*/
|
|
1478
|
+
trackByEventId(_index, event) {
|
|
1479
|
+
return event.id;
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Track by function for segments.
|
|
1483
|
+
*/
|
|
1484
|
+
trackBySegmentLabel(_index, segment) {
|
|
1485
|
+
return segment.label;
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Gets the global index of an event in the allEvents array.
|
|
1489
|
+
*/
|
|
1490
|
+
getGlobalIndex(event) {
|
|
1491
|
+
return this.allEvents.indexOf(event);
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Checks if an event is currently selected/focused.
|
|
1495
|
+
* An event is selected if either:
|
|
1496
|
+
* - Its ID matches the selectedEventId input
|
|
1497
|
+
* - Its global index matches the focusedEventIndex (keyboard navigation)
|
|
1498
|
+
*/
|
|
1499
|
+
isEventSelected(event, globalIndex) {
|
|
1500
|
+
// Check selectedEventId from parent first (takes priority)
|
|
1501
|
+
if (this.selectedEventId && event.id === this.selectedEventId) {
|
|
1502
|
+
return true;
|
|
1503
|
+
}
|
|
1504
|
+
// Fall back to keyboard navigation focus
|
|
1505
|
+
return this.focusedEventIndex === globalIndex;
|
|
1506
|
+
}
|
|
1507
|
+
// ============================================================================
|
|
1508
|
+
// PRIVATE METHODS - DATA LOADING
|
|
1509
|
+
// ============================================================================
|
|
1510
|
+
/**
|
|
1511
|
+
* Loads data from all groups.
|
|
1512
|
+
*/
|
|
1513
|
+
async loadAllGroups() {
|
|
1514
|
+
for (let groupIndex = 0; groupIndex < this._groups.length; groupIndex++) {
|
|
1515
|
+
const group = this._groups[groupIndex];
|
|
1516
|
+
await this.loadGroup(group, groupIndex);
|
|
1517
|
+
}
|
|
1518
|
+
// Sort all events by date
|
|
1519
|
+
this.sortEvents();
|
|
1520
|
+
// Update scroll state
|
|
1521
|
+
this.scrollState.loadedCount = this.allEvents.length;
|
|
1522
|
+
this.scrollState.hasMore = false; // TODO: Implement proper pagination detection
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Loads data from a single group.
|
|
1526
|
+
*/
|
|
1527
|
+
async loadGroup(group, groupIndex) {
|
|
1528
|
+
let records = [];
|
|
1529
|
+
if (group.DataSourceType === 'array') {
|
|
1530
|
+
records = group.EntityObjects || [];
|
|
1531
|
+
}
|
|
1532
|
+
else if (group.DataSourceType === 'entity' && group.EntityName) {
|
|
1533
|
+
records = await this.loadFromEntity(group);
|
|
1534
|
+
}
|
|
1535
|
+
// Convert records to timeline events
|
|
1536
|
+
for (const record of records) {
|
|
1537
|
+
const event = this.createTimelineEvent(record, group, groupIndex);
|
|
1538
|
+
this.allEvents.push(event);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Loads data from MemberJunction entity.
|
|
1543
|
+
*/
|
|
1544
|
+
async loadFromEntity(group) {
|
|
1545
|
+
try {
|
|
1546
|
+
const { RunView } = await import('@memberjunction/core');
|
|
1547
|
+
const rv = new RunView();
|
|
1548
|
+
const result = await rv.RunView({
|
|
1549
|
+
EntityName: group.EntityName,
|
|
1550
|
+
ExtraFilter: group.Filter,
|
|
1551
|
+
OrderBy: group.OrderBy,
|
|
1552
|
+
MaxRows: group.MaxRecords,
|
|
1553
|
+
ResultType: 'entity_object'
|
|
1554
|
+
});
|
|
1555
|
+
if (result?.Success) {
|
|
1556
|
+
return result.Results;
|
|
164
1557
|
}
|
|
165
|
-
|
|
166
|
-
|
|
1558
|
+
}
|
|
1559
|
+
catch (error) {
|
|
1560
|
+
console.warn('Timeline: Could not load from entity. Is @memberjunction/core available?', error);
|
|
1561
|
+
}
|
|
1562
|
+
return [];
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Loads the next batch for virtual scrolling.
|
|
1566
|
+
*/
|
|
1567
|
+
async loadNextBatch() {
|
|
1568
|
+
// TODO: Implement proper batch loading with offset/limit
|
|
1569
|
+
// For now, this is a placeholder
|
|
1570
|
+
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Creates a timeline event from a source record.
|
|
1573
|
+
*/
|
|
1574
|
+
createTimelineEvent(record, group, groupIndex) {
|
|
1575
|
+
const cardConfig = group.getEffectiveCardConfig();
|
|
1576
|
+
return {
|
|
1577
|
+
id: group.getId(record),
|
|
1578
|
+
entity: record,
|
|
1579
|
+
title: group.getTitle(record),
|
|
1580
|
+
date: group.getDate(record),
|
|
1581
|
+
subtitle: group.getSubtitle(record),
|
|
1582
|
+
description: group.getDescription(record),
|
|
1583
|
+
imageUrl: group.getImageUrl(record),
|
|
1584
|
+
config: group.getEventConfig(record),
|
|
1585
|
+
groupIndex,
|
|
1586
|
+
isExpanded: cardConfig.defaultExpanded ?? false
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Sorts events by date according to sortOrder.
|
|
1591
|
+
*/
|
|
1592
|
+
sortEvents() {
|
|
1593
|
+
this.allEvents.sort((a, b) => {
|
|
1594
|
+
const diff = a.date.getTime() - b.date.getTime();
|
|
1595
|
+
return this.sortOrder === 'asc' ? diff : -diff;
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
// ============================================================================
|
|
1599
|
+
// PRIVATE METHODS - SEGMENTATION
|
|
1600
|
+
// ============================================================================
|
|
1601
|
+
/**
|
|
1602
|
+
* Builds time segments from events.
|
|
1603
|
+
*/
|
|
1604
|
+
buildSegments() {
|
|
1605
|
+
if (this.segmentGrouping === 'none') {
|
|
1606
|
+
this.segments = [];
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
const segmentMap = new Map();
|
|
1610
|
+
for (const event of this.allEvents) {
|
|
1611
|
+
const { label, startDate, endDate } = this.getSegmentInfo(event.date);
|
|
1612
|
+
if (!segmentMap.has(label)) {
|
|
1613
|
+
segmentMap.set(label, {
|
|
1614
|
+
label,
|
|
1615
|
+
startDate,
|
|
1616
|
+
endDate,
|
|
1617
|
+
events: [],
|
|
1618
|
+
isExpanded: this.segmentsDefaultExpanded,
|
|
1619
|
+
eventCount: 0
|
|
1620
|
+
});
|
|
167
1621
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
1622
|
+
const segment = segmentMap.get(label);
|
|
1623
|
+
segment.events.push(event);
|
|
1624
|
+
segment.eventCount++;
|
|
1625
|
+
}
|
|
1626
|
+
// Convert to array and sort
|
|
1627
|
+
this.segments = Array.from(segmentMap.values());
|
|
1628
|
+
this.segments.sort((a, b) => {
|
|
1629
|
+
const diff = a.startDate.getTime() - b.startDate.getTime();
|
|
1630
|
+
return this.sortOrder === 'asc' ? diff : -diff;
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Gets segment information for a date.
|
|
1635
|
+
*/
|
|
1636
|
+
getSegmentInfo(date) {
|
|
1637
|
+
const year = date.getFullYear();
|
|
1638
|
+
const month = date.getMonth();
|
|
1639
|
+
const day = date.getDate();
|
|
1640
|
+
switch (this.segmentGrouping) {
|
|
1641
|
+
case 'day':
|
|
1642
|
+
return {
|
|
1643
|
+
label: this.formatDateInternal(date, 'MMMM d, yyyy'),
|
|
1644
|
+
startDate: new Date(year, month, day),
|
|
1645
|
+
endDate: new Date(year, month, day + 1)
|
|
1646
|
+
};
|
|
1647
|
+
case 'week':
|
|
1648
|
+
const weekStart = new Date(date);
|
|
1649
|
+
weekStart.setDate(day - date.getDay());
|
|
1650
|
+
const weekEnd = new Date(weekStart);
|
|
1651
|
+
weekEnd.setDate(weekStart.getDate() + 7);
|
|
1652
|
+
return {
|
|
1653
|
+
label: `Week of ${this.formatDateInternal(weekStart, 'MMM d, yyyy')}`,
|
|
1654
|
+
startDate: weekStart,
|
|
1655
|
+
endDate: weekEnd
|
|
1656
|
+
};
|
|
1657
|
+
case 'month':
|
|
1658
|
+
return {
|
|
1659
|
+
label: this.formatDateInternal(new Date(year, month, 1), 'MMMM yyyy'),
|
|
1660
|
+
startDate: new Date(year, month, 1),
|
|
1661
|
+
endDate: new Date(year, month + 1, 1)
|
|
1662
|
+
};
|
|
1663
|
+
case 'quarter':
|
|
1664
|
+
const quarter = Math.floor(month / 3);
|
|
1665
|
+
return {
|
|
1666
|
+
label: `Q${quarter + 1} ${year}`,
|
|
1667
|
+
startDate: new Date(year, quarter * 3, 1),
|
|
1668
|
+
endDate: new Date(year, (quarter + 1) * 3, 1)
|
|
1669
|
+
};
|
|
1670
|
+
case 'year':
|
|
1671
|
+
return {
|
|
1672
|
+
label: String(year),
|
|
1673
|
+
startDate: new Date(year, 0, 1),
|
|
1674
|
+
endDate: new Date(year + 1, 0, 1)
|
|
1675
|
+
};
|
|
1676
|
+
default:
|
|
1677
|
+
return {
|
|
1678
|
+
label: this.formatDateInternal(date, 'MMMM yyyy'),
|
|
1679
|
+
startDate: new Date(year, month, 1),
|
|
1680
|
+
endDate: new Date(year, month + 1, 1)
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
// ============================================================================
|
|
1685
|
+
// PRIVATE METHODS - EXPAND/COLLAPSE
|
|
1686
|
+
// ============================================================================
|
|
1687
|
+
/**
|
|
1688
|
+
* Toggles event expanded state.
|
|
1689
|
+
*/
|
|
1690
|
+
toggleEventExpanded(event, index, domEvent) {
|
|
1691
|
+
if (event.isExpanded) {
|
|
1692
|
+
this.collapseEventInternal(event, index, domEvent);
|
|
1693
|
+
}
|
|
1694
|
+
else {
|
|
1695
|
+
this.expandEventInternal(event, index, domEvent);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
/**
|
|
1699
|
+
* Sets event expanded state.
|
|
1700
|
+
*/
|
|
1701
|
+
setEventExpanded(event, expanded) {
|
|
1702
|
+
event.isExpanded = expanded;
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Expands an event with events.
|
|
1706
|
+
*/
|
|
1707
|
+
expandEventInternal(event, index, domEvent) {
|
|
1708
|
+
const group = this._groups[event.groupIndex];
|
|
1709
|
+
const beforeArgs = {
|
|
1710
|
+
cancel: false,
|
|
1711
|
+
event,
|
|
1712
|
+
group,
|
|
1713
|
+
index,
|
|
1714
|
+
domEvent
|
|
1715
|
+
};
|
|
1716
|
+
this.beforeEventExpand.emit(beforeArgs);
|
|
1717
|
+
if (beforeArgs.cancel) {
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
event.isExpanded = true;
|
|
1721
|
+
this.cdr.markForCheck();
|
|
1722
|
+
const afterArgs = {
|
|
1723
|
+
success: true,
|
|
1724
|
+
event,
|
|
1725
|
+
group,
|
|
1726
|
+
index,
|
|
1727
|
+
domEvent
|
|
1728
|
+
};
|
|
1729
|
+
this.afterEventExpand.emit(afterArgs);
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* Collapses an event with events.
|
|
1733
|
+
*/
|
|
1734
|
+
collapseEventInternal(event, index, domEvent) {
|
|
1735
|
+
const group = this._groups[event.groupIndex];
|
|
1736
|
+
const beforeArgs = {
|
|
1737
|
+
cancel: false,
|
|
1738
|
+
event,
|
|
1739
|
+
group,
|
|
1740
|
+
index,
|
|
1741
|
+
domEvent
|
|
1742
|
+
};
|
|
1743
|
+
this.beforeEventCollapse.emit(beforeArgs);
|
|
1744
|
+
if (beforeArgs.cancel) {
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
event.isExpanded = false;
|
|
1748
|
+
this.cdr.markForCheck();
|
|
1749
|
+
const afterArgs = {
|
|
1750
|
+
success: true,
|
|
1751
|
+
event,
|
|
1752
|
+
group,
|
|
1753
|
+
index,
|
|
1754
|
+
domEvent
|
|
1755
|
+
};
|
|
1756
|
+
this.afterEventCollapse.emit(afterArgs);
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Expands a segment.
|
|
1760
|
+
*/
|
|
1761
|
+
expandSegment(segment) {
|
|
1762
|
+
const beforeArgs = {
|
|
1763
|
+
cancel: false,
|
|
1764
|
+
segment,
|
|
1765
|
+
label: segment.label,
|
|
1766
|
+
startDate: segment.startDate,
|
|
1767
|
+
endDate: segment.endDate,
|
|
1768
|
+
eventCount: segment.eventCount
|
|
1769
|
+
};
|
|
1770
|
+
this.beforeSegmentExpand.emit(beforeArgs);
|
|
1771
|
+
if (beforeArgs.cancel) {
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
segment.isExpanded = true;
|
|
1775
|
+
this.cdr.markForCheck();
|
|
1776
|
+
const afterArgs = {
|
|
1777
|
+
success: true,
|
|
1778
|
+
segment,
|
|
1779
|
+
label: segment.label,
|
|
1780
|
+
startDate: segment.startDate,
|
|
1781
|
+
endDate: segment.endDate,
|
|
1782
|
+
eventCount: segment.eventCount
|
|
1783
|
+
};
|
|
1784
|
+
this.afterSegmentExpand.emit(afterArgs);
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* Collapses a segment.
|
|
1788
|
+
*/
|
|
1789
|
+
collapseSegment(segment) {
|
|
1790
|
+
const beforeArgs = {
|
|
1791
|
+
cancel: false,
|
|
1792
|
+
segment,
|
|
1793
|
+
label: segment.label,
|
|
1794
|
+
startDate: segment.startDate,
|
|
1795
|
+
endDate: segment.endDate,
|
|
1796
|
+
eventCount: segment.eventCount
|
|
1797
|
+
};
|
|
1798
|
+
this.beforeSegmentCollapse.emit(beforeArgs);
|
|
1799
|
+
if (beforeArgs.cancel) {
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
segment.isExpanded = false;
|
|
1803
|
+
this.cdr.markForCheck();
|
|
1804
|
+
const afterArgs = {
|
|
1805
|
+
success: true,
|
|
1806
|
+
segment,
|
|
1807
|
+
label: segment.label,
|
|
1808
|
+
startDate: segment.startDate,
|
|
1809
|
+
endDate: segment.endDate,
|
|
1810
|
+
eventCount: segment.eventCount
|
|
1811
|
+
};
|
|
1812
|
+
this.afterSegmentCollapse.emit(afterArgs);
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Sets segment expanded state without events.
|
|
1816
|
+
*/
|
|
1817
|
+
setSegmentExpanded(segment, expanded) {
|
|
1818
|
+
segment.isExpanded = expanded;
|
|
1819
|
+
}
|
|
1820
|
+
// ============================================================================
|
|
1821
|
+
// PRIVATE METHODS - KEYBOARD NAVIGATION
|
|
1822
|
+
// ============================================================================
|
|
1823
|
+
focusNextEvent() {
|
|
1824
|
+
if (this.allEvents.length === 0)
|
|
1825
|
+
return;
|
|
1826
|
+
this.focusedEventIndex = Math.min(this.focusedEventIndex + 1, this.allEvents.length - 1);
|
|
1827
|
+
this.scrollToFocusedEvent();
|
|
1828
|
+
}
|
|
1829
|
+
focusPreviousEvent() {
|
|
1830
|
+
if (this.allEvents.length === 0)
|
|
1831
|
+
return;
|
|
1832
|
+
this.focusedEventIndex = Math.max(this.focusedEventIndex - 1, 0);
|
|
1833
|
+
this.scrollToFocusedEvent();
|
|
1834
|
+
}
|
|
1835
|
+
focusFirstEvent() {
|
|
1836
|
+
if (this.allEvents.length === 0)
|
|
1837
|
+
return;
|
|
1838
|
+
this.focusedEventIndex = 0;
|
|
1839
|
+
this.scrollToFocusedEvent();
|
|
1840
|
+
}
|
|
1841
|
+
focusLastEvent() {
|
|
1842
|
+
if (this.allEvents.length === 0)
|
|
1843
|
+
return;
|
|
1844
|
+
this.focusedEventIndex = this.allEvents.length - 1;
|
|
1845
|
+
this.scrollToFocusedEvent();
|
|
1846
|
+
}
|
|
1847
|
+
activateFocusedEvent() {
|
|
1848
|
+
if (this.focusedEventIndex >= 0 && this.focusedEventIndex < this.allEvents.length) {
|
|
1849
|
+
const event = this.allEvents[this.focusedEventIndex];
|
|
1850
|
+
this.toggleEventExpanded(event, this.focusedEventIndex);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
collapseFocusedEvent() {
|
|
1854
|
+
if (this.focusedEventIndex >= 0 && this.focusedEventIndex < this.allEvents.length) {
|
|
1855
|
+
const event = this.allEvents[this.focusedEventIndex];
|
|
1856
|
+
if (event.isExpanded) {
|
|
1857
|
+
this.collapseEventInternal(event, this.focusedEventIndex);
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
scrollToFocusedEvent() {
|
|
1862
|
+
if (this.focusedEventIndex >= 0 && this.focusedEventIndex < this.allEvents.length) {
|
|
1863
|
+
const event = this.allEvents[this.focusedEventIndex];
|
|
1864
|
+
this.scrollToEvent(event.id, 'smooth');
|
|
1865
|
+
}
|
|
1866
|
+
this.cdr.markForCheck();
|
|
1867
|
+
}
|
|
1868
|
+
// ============================================================================
|
|
1869
|
+
// PRIVATE METHODS - VIRTUAL SCROLLING
|
|
1870
|
+
// ============================================================================
|
|
1871
|
+
setupIntersectionObserver() {
|
|
1872
|
+
if (!this.virtualScroll.enabled)
|
|
1873
|
+
return;
|
|
1874
|
+
// Use requestAnimationFrame to ensure DOM is ready
|
|
1875
|
+
requestAnimationFrame(() => {
|
|
1876
|
+
const sentinel = this.elementRef.nativeElement.querySelector('.mj-timeline-scroll-sentinel');
|
|
1877
|
+
if (!sentinel)
|
|
1878
|
+
return;
|
|
1879
|
+
this._intersectionObserver = new IntersectionObserver((entries) => {
|
|
1880
|
+
const entry = entries[0];
|
|
1881
|
+
if (entry?.isIntersecting && !this.scrollState.isLoading && this.scrollState.hasMore) {
|
|
1882
|
+
this.ngZone.run(() => this.loadMore());
|
|
1883
|
+
}
|
|
1884
|
+
}, {
|
|
1885
|
+
root: this.scrollContainer?.nativeElement,
|
|
1886
|
+
threshold: 0,
|
|
1887
|
+
rootMargin: `${this.virtualScroll.loadThreshold}px`
|
|
1888
|
+
});
|
|
1889
|
+
this._intersectionObserver.observe(sentinel);
|
|
176
1890
|
});
|
|
177
|
-
return ret;
|
|
178
1891
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
1892
|
+
onScrollCheck() {
|
|
1893
|
+
if (!this.scrollContainer?.nativeElement || !this.virtualScroll.enabled) {
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
const el = this.scrollContainer.nativeElement;
|
|
1897
|
+
this.scrollState.scrollOffset = el.scrollTop;
|
|
1898
|
+
const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
1899
|
+
if (distanceFromBottom < this.virtualScroll.loadThreshold && this.scrollState.hasMore) {
|
|
1900
|
+
this.loadMore();
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
// ============================================================================
|
|
1904
|
+
// PRIVATE METHODS - UTILITIES
|
|
1905
|
+
// ============================================================================
|
|
1906
|
+
/**
|
|
1907
|
+
* Maps event config to card config properties.
|
|
1908
|
+
*/
|
|
1909
|
+
mapEventConfigToCardConfig(config = {}) {
|
|
1910
|
+
return {
|
|
1911
|
+
collapsible: config.collapsible,
|
|
1912
|
+
defaultExpanded: config.defaultExpanded,
|
|
1913
|
+
actions: config.actions
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Simple date formatter (replaces Angular DatePipe for standalone use).
|
|
1918
|
+
* Uses placeholder tokens to avoid replacement conflicts.
|
|
1919
|
+
*/
|
|
1920
|
+
formatDateInternal(date, format) {
|
|
1921
|
+
const months = ['January', 'February', 'March', 'April', 'May', 'June',
|
|
1922
|
+
'July', 'August', 'September', 'October', 'November', 'December'];
|
|
1923
|
+
const monthsShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
1924
|
+
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
1925
|
+
const year = date.getFullYear();
|
|
1926
|
+
const month = date.getMonth();
|
|
1927
|
+
const day = date.getDate();
|
|
1928
|
+
const hours = date.getHours();
|
|
1929
|
+
const minutes = date.getMinutes();
|
|
1930
|
+
// Use placeholder tokens to avoid conflicts (e.g., 'May' containing 'M')
|
|
1931
|
+
// Replace longer patterns first with placeholders, then substitute values
|
|
1932
|
+
let result = format;
|
|
1933
|
+
// Replace patterns with unique placeholders first
|
|
1934
|
+
result = result.replace(/yyyy/g, '{{YEAR}}');
|
|
1935
|
+
result = result.replace(/MMMM/g, '{{MONTH_FULL}}');
|
|
1936
|
+
result = result.replace(/MMM/g, '{{MONTH_SHORT}}');
|
|
1937
|
+
result = result.replace(/MM/g, '{{MONTH_PAD}}');
|
|
1938
|
+
result = result.replace(/dd/g, '{{DAY_PAD}}');
|
|
1939
|
+
result = result.replace(/d/g, '{{DAY}}');
|
|
1940
|
+
result = result.replace(/HH/g, '{{HOUR_24}}');
|
|
1941
|
+
result = result.replace(/hh/g, '{{HOUR_12_PAD}}');
|
|
1942
|
+
result = result.replace(/h/g, '{{HOUR_12}}');
|
|
1943
|
+
result = result.replace(/mm/g, '{{MIN}}');
|
|
1944
|
+
result = result.replace(/a/g, '{{AMPM}}');
|
|
1945
|
+
// Now substitute the actual values
|
|
1946
|
+
result = result.replace(/\{\{YEAR\}\}/g, String(year));
|
|
1947
|
+
result = result.replace(/\{\{MONTH_FULL\}\}/g, months[month]);
|
|
1948
|
+
result = result.replace(/\{\{MONTH_SHORT\}\}/g, monthsShort[month]);
|
|
1949
|
+
result = result.replace(/\{\{MONTH_PAD\}\}/g, String(month + 1).padStart(2, '0'));
|
|
1950
|
+
result = result.replace(/\{\{DAY_PAD\}\}/g, String(day).padStart(2, '0'));
|
|
1951
|
+
result = result.replace(/\{\{DAY\}\}/g, String(day));
|
|
1952
|
+
result = result.replace(/\{\{HOUR_24\}\}/g, String(hours).padStart(2, '0'));
|
|
1953
|
+
result = result.replace(/\{\{HOUR_12_PAD\}\}/g, String(hours % 12 || 12).padStart(2, '0'));
|
|
1954
|
+
result = result.replace(/\{\{HOUR_12\}\}/g, String(hours % 12 || 12));
|
|
1955
|
+
result = result.replace(/\{\{MIN\}\}/g, String(minutes).padStart(2, '0'));
|
|
1956
|
+
result = result.replace(/\{\{AMPM\}\}/g, hours >= 12 ? 'PM' : 'AM');
|
|
1957
|
+
return result;
|
|
1958
|
+
}
|
|
1959
|
+
static ɵfac = function TimelineComponent_Factory(t) { return new (t || TimelineComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.NgZone)); };
|
|
1960
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TimelineComponent, selectors: [["mj-timeline"]], contentQueries: function TimelineComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) {
|
|
1961
|
+
i0.ɵɵcontentQuery(dirIndex, _c0, 5);
|
|
1962
|
+
i0.ɵɵcontentQuery(dirIndex, _c1, 5);
|
|
1963
|
+
i0.ɵɵcontentQuery(dirIndex, _c2, 5);
|
|
1964
|
+
i0.ɵɵcontentQuery(dirIndex, _c3, 5);
|
|
1965
|
+
i0.ɵɵcontentQuery(dirIndex, _c4, 5);
|
|
1966
|
+
i0.ɵɵcontentQuery(dirIndex, _c5, 5);
|
|
1967
|
+
i0.ɵɵcontentQuery(dirIndex, _c6, 5);
|
|
1968
|
+
} if (rf & 2) {
|
|
1969
|
+
let _t;
|
|
1970
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.cardTemplate = _t.first);
|
|
1971
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.headerTemplate = _t.first);
|
|
1972
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.bodyTemplate = _t.first);
|
|
1973
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.actionsTemplate = _t.first);
|
|
1974
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.segmentHeaderTemplate = _t.first);
|
|
1975
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.emptyTemplate = _t.first);
|
|
1976
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.loadingTemplate = _t.first);
|
|
1977
|
+
} }, viewQuery: function TimelineComponent_Query(rf, ctx) { if (rf & 1) {
|
|
1978
|
+
i0.ɵɵviewQuery(_c7, 5);
|
|
1979
|
+
} if (rf & 2) {
|
|
1980
|
+
let _t;
|
|
1981
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.scrollContainer = _t.first);
|
|
1982
|
+
} }, inputs: { groups: "groups", allowLoad: "allowLoad", orientation: "orientation", layout: "layout", sortOrder: "sortOrder", segmentGrouping: "segmentGrouping", defaultCardConfig: "defaultCardConfig", virtualScroll: "virtualScroll", segmentsCollapsible: "segmentsCollapsible", segmentsDefaultExpanded: "segmentsDefaultExpanded", emptyMessage: "emptyMessage", emptyIcon: "emptyIcon", loadingMessage: "loadingMessage", ariaLabel: "ariaLabel", enableKeyboardNavigation: "enableKeyboardNavigation", selectedEventId: "selectedEventId" }, outputs: { beforeEventClick: "beforeEventClick", beforeEventExpand: "beforeEventExpand", beforeEventCollapse: "beforeEventCollapse", beforeEventHover: "beforeEventHover", beforeActionClick: "beforeActionClick", beforeSegmentExpand: "beforeSegmentExpand", beforeSegmentCollapse: "beforeSegmentCollapse", beforeLoad: "beforeLoad", afterEventClick: "afterEventClick", afterEventExpand: "afterEventExpand", afterEventCollapse: "afterEventCollapse", afterEventHover: "afterEventHover", afterActionClick: "afterActionClick", afterSegmentExpand: "afterSegmentExpand", afterSegmentCollapse: "afterSegmentCollapse", afterLoad: "afterLoad" }, decls: 9, vars: 14, consts: [["scrollContainer", ""], ["eventCard", ""], ["defaultLoading", ""], ["defaultEmpty", ""], ["defaultSegmentHeader", ""], ["defaultCard", ""], ["defaultHeader", ""], ["defaultBody", ""], ["plainDescription", ""], ["defaultActions", ""], ["role", "list", "tabindex", "0", 1, "mj-timeline", 3, "keydown", "scroll"], [4, "ngIf"], ["class", "mj-timeline-scroll-sentinel", 4, "ngIf"], ["class", "mj-timeline__loading-more", 4, "ngIf"], [4, "ngIf", "ngIfElse"], [4, "ngTemplateOutlet"], [1, "mj-timeline__loading"], [1, "mj-timeline__loading-spinner"], [1, "mj-timeline__loading-text"], [1, "mj-timeline__empty"], [1, "mj-timeline__empty-icon"], [1, "mj-timeline__empty-text"], ["class", "mj-timeline__segment", 3, "mj-timeline__segment--collapsed", 4, "ngFor", "ngForOf", "ngForTrackBy"], [1, "mj-timeline__segment"], ["role", "button", 1, "mj-timeline__segment-header", 3, "click"], [1, "mj-timeline__segment-content", 3, "id"], [1, "mj-timeline__axis"], [4, "ngFor", "ngForOf", "ngForTrackBy"], [4, "ngTemplateOutlet", "ngTemplateOutletContext"], ["class", "mj-timeline__segment-toggle", 4, "ngIf"], [1, "mj-timeline__segment-label"], [1, "mj-timeline__segment-count"], [1, "mj-timeline__segment-line"], [1, "mj-timeline__segment-toggle"], [1, "mj-timeline-scroll-sentinel"], [1, "mj-timeline__loading-more"], [1, "mj-timeline__loading-spinner", "mj-timeline__loading-spinner--small"], ["role", "listitem", 1, "mj-timeline__event"], [1, "mj-timeline__marker"], [1, "mj-timeline__marker-icon"], [1, "mj-timeline__connector"], ["class", "mj-timeline__date-label", 4, "ngIf"], [1, "mj-timeline__card", 3, "click", "mouseenter", "mouseleave", "ngClass"], [1, "mj-timeline__date-label"], [1, "mj-timeline__card-header"], ["class", "mj-timeline__card-image mj-timeline__card-image--left", 3, "mj-timeline__card-image--small", "mj-timeline__card-image--medium", "mj-timeline__card-image--large", 4, "ngIf"], [1, "mj-timeline__card-header-content"], ["class", "mj-timeline__card-image mj-timeline__card-image--top", 4, "ngIf"], [1, "mj-timeline__card-body"], ["class", "mj-timeline__card-actions", 3, "mj-timeline__card-actions--hover-only", 4, "ngIf"], [1, "mj-timeline__card-image", "mj-timeline__card-image--left"], [3, "src", "alt"], ["class", "mj-timeline__card-icon", 3, "color", 4, "ngIf"], [1, "mj-timeline__card-titles"], [1, "mj-timeline__card-title"], ["class", "mj-timeline__card-subtitle", 4, "ngIf"], ["class", "mj-timeline__card-date", 4, "ngIf"], ["class", "mj-timeline__card-toggle", "type", "button", 3, "click", 4, "ngIf"], [1, "mj-timeline__card-icon"], [1, "mj-timeline__card-subtitle"], [1, "mj-timeline__card-date"], ["type", "button", 1, "mj-timeline__card-toggle", 3, "click"], [1, "mj-timeline__card-image", "mj-timeline__card-image--top"], ["class", "mj-timeline__card-fields mj-timeline__card-fields--summary", 4, "ngIf"], ["class", "mj-timeline__card-description", 3, "mj-timeline__card-description--clamped", "-webkit-line-clamp", 4, "ngIf"], ["class", "mj-timeline__card-fields mj-timeline__card-fields--expanded", 4, "ngIf"], [1, "mj-timeline__card-fields", "mj-timeline__card-fields--summary"], [4, "ngFor", "ngForOf"], [1, "mj-timeline__card-field", 3, "ngClass"], ["class", "mj-timeline__card-field-icon", 3, "ngClass", 4, "ngIf"], ["class", "mj-timeline__card-field-label", 4, "ngIf"], [1, "mj-timeline__card-field-value"], [1, "mj-timeline__card-field-icon", 3, "ngClass"], [1, "mj-timeline__card-field-label"], [1, "mj-timeline__card-description"], [3, "innerHTML"], [1, "mj-timeline__card-fields", "mj-timeline__card-fields--expanded"], [1, "mj-timeline__card-actions"], ["class", "mj-timeline__action", "type", "button", 3, "mj-timeline__action--primary", "mj-timeline__action--secondary", "mj-timeline__action--danger", "mj-timeline__action--link", "ngClass", "disabled", "title", "click", 4, "ngFor", "ngForOf"], ["type", "button", 1, "mj-timeline__action", 3, "click", "ngClass", "disabled", "title"], [3, "ngClass", 4, "ngIf"], [3, "ngClass"]], template: function TimelineComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1983
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
1984
|
+
i0.ɵɵelementStart(0, "div", 10, 0);
|
|
1985
|
+
i0.ɵɵlistener("keydown", function TimelineComponent_Template_div_keydown_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onKeyDown($event)); })("scroll", function TimelineComponent_Template_div_scroll_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onScroll($event)); });
|
|
1986
|
+
i0.ɵɵtemplate(2, TimelineComponent_ng_container_2_Template, 4, 2, "ng-container", 11)(3, TimelineComponent_ng_container_3_Template, 4, 2, "ng-container", 11)(4, TimelineComponent_ng_container_4_Template, 3, 2, "ng-container", 11)(5, TimelineComponent_div_5_Template, 1, 0, "div", 12)(6, TimelineComponent_div_6_Template, 4, 1, "div", 13);
|
|
183
1987
|
i0.ɵɵelementEnd();
|
|
1988
|
+
i0.ɵɵtemplate(7, TimelineComponent_ng_template_7_Template, 9, 26, "ng-template", null, 1, i0.ɵɵtemplateRefExtractor);
|
|
184
1989
|
} if (rf & 2) {
|
|
1990
|
+
i0.ɵɵclassProp("mj-timeline--vertical", ctx.orientation === "vertical")("mj-timeline--horizontal", ctx.orientation === "horizontal")("mj-timeline--single", ctx.layout === "single")("mj-timeline--alternating", ctx.layout === "alternating");
|
|
1991
|
+
i0.ɵɵattribute("aria-label", ctx.ariaLabel);
|
|
1992
|
+
i0.ɵɵadvance(2);
|
|
1993
|
+
i0.ɵɵproperty("ngIf", ctx.isLoading && !ctx.isInitialized);
|
|
1994
|
+
i0.ɵɵadvance();
|
|
1995
|
+
i0.ɵɵproperty("ngIf", ctx.isInitialized && ctx.allEvents.length === 0 && !ctx.isLoading);
|
|
1996
|
+
i0.ɵɵadvance();
|
|
1997
|
+
i0.ɵɵproperty("ngIf", ctx.isInitialized && ctx.allEvents.length > 0);
|
|
1998
|
+
i0.ɵɵadvance();
|
|
1999
|
+
i0.ɵɵproperty("ngIf", ctx.virtualScroll.enabled && ctx.scrollState.hasMore);
|
|
185
2000
|
i0.ɵɵadvance();
|
|
186
|
-
i0.ɵɵproperty("
|
|
187
|
-
} }, dependencies: [i1.TimelineComponent] });
|
|
2001
|
+
i0.ɵɵproperty("ngIf", ctx.scrollState.isLoading && ctx.virtualScroll.showLoadingIndicator);
|
|
2002
|
+
} }, dependencies: [i1.NgClass, i1.NgForOf, i1.NgIf, i1.NgTemplateOutlet], styles: ["/**\n * MJ Timeline Component Styles\n * Kendo-inspired design with vertical alternating and horizontal layouts\n */\n\n/* ============================================================================\n CSS VARIABLES (Theming)\n ============================================================================ */\n\nmj-timeline {\n /* Colors */\n --mj-timeline-bg: transparent;\n --mj-timeline-line-color: #d0d7de;\n --mj-timeline-marker-bg: #ffffff;\n --mj-timeline-marker-border: #4678a8;\n --mj-timeline-marker-fill: #4678a8;\n --mj-timeline-card-bg: #ffffff;\n --mj-timeline-card-border: #d0d7de;\n --mj-timeline-card-shadow: 0 1px 3px rgba(31, 35, 40, 0.08);\n --mj-timeline-card-shadow-hover: 0 4px 12px rgba(31, 35, 40, 0.12);\n --mj-timeline-card-radius: 6px;\n --mj-timeline-text-primary: #1f2328;\n --mj-timeline-text-secondary: #656d76;\n --mj-timeline-text-muted: #8c959f;\n --mj-timeline-accent: #4678a8;\n --mj-timeline-accent-light: rgba(70, 120, 168, 0.08);\n --mj-timeline-segment-bg: #4678a8;\n --mj-timeline-segment-text: #ffffff;\n --mj-timeline-focus-ring: 0 0 0 2px rgba(70, 120, 168, 0.3);\n\n /* Sizing */\n --mj-timeline-line-width: 2px;\n --mj-timeline-marker-size: 14px;\n --mj-timeline-card-padding: 16px;\n --mj-timeline-card-max-width: 400px;\n --mj-timeline-card-min-width: 200px;\n --mj-timeline-gap: 24px;\n --mj-timeline-segment-gap: 16px;\n --mj-timeline-axis-offset: 50%;\n\n /* Animation */\n --mj-timeline-transition: 0.15s ease;\n\n display: block;\n width: 100%;\n height: 100%;\n}\n\n/* Dark mode */\n.dark-theme mj-timeline,\n[data-theme=\"dark\"] mj-timeline {\n --mj-timeline-line-color: #3d444d;\n --mj-timeline-card-bg: #161b22;\n --mj-timeline-card-border: #3d444d;\n --mj-timeline-card-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n --mj-timeline-text-primary: #e6edf3;\n --mj-timeline-text-secondary: #8b949e;\n --mj-timeline-text-muted: #6e7681;\n --mj-timeline-accent: #58a6ff;\n --mj-timeline-accent-light: rgba(88, 166, 255, 0.1);\n --mj-timeline-marker-border: #58a6ff;\n --mj-timeline-marker-fill: #58a6ff;\n --mj-timeline-segment-bg: #58a6ff;\n}\n\n/* ============================================================================\n MAIN CONTAINER\n ============================================================================ */\n\n.mj-timeline {\n position: relative;\n width: 100%;\n height: 100%;\n padding: 0;\n background: var(--mj-timeline-bg);\n outline: none;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n/* Subtle scrollbar */\n.mj-timeline::-webkit-scrollbar {\n width: 6px;\n}\n\n.mj-timeline::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.mj-timeline::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.15);\n border-radius: 3px;\n}\n\n.mj-timeline::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.25);\n}\n\n.mj-timeline:focus-visible {\n box-shadow: inset var(--mj-timeline-focus-ring);\n}\n\n/* ============================================================================\n LOADING & EMPTY STATES\n ============================================================================ */\n\n.mj-timeline__loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n gap: 12px;\n}\n\n.mj-timeline__loading-spinner {\n width: 24px;\n height: 24px;\n border: 2px solid var(--mj-timeline-line-color);\n border-top-color: var(--mj-timeline-accent);\n border-radius: 50%;\n animation: mj-timeline-spin 0.6s linear infinite;\n}\n\n.mj-timeline__loading-spinner--small {\n width: 14px;\n height: 14px;\n}\n\n.mj-timeline__loading-text {\n color: var(--mj-timeline-text-muted);\n font-size: 13px;\n}\n\n.mj-timeline__loading-more {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n gap: 8px;\n color: var(--mj-timeline-text-muted);\n font-size: 12px;\n}\n\n@keyframes mj-timeline-spin {\n to { transform: rotate(360deg); }\n}\n\n.mj-timeline__empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 64px 24px;\n gap: 8px;\n text-align: center;\n}\n\n.mj-timeline__empty-icon {\n font-size: 32px;\n color: var(--mj-timeline-text-muted);\n opacity: 0.4;\n}\n\n.mj-timeline__empty-text {\n color: var(--mj-timeline-text-secondary);\n font-size: 13px;\n}\n\n/* ============================================================================\n TIME SEGMENTS - Year/Month badges on the axis\n ============================================================================ */\n\n.mj-timeline__segment {\n margin-bottom: var(--mj-timeline-segment-gap);\n position: relative;\n}\n\n.mj-timeline__segment:last-child {\n margin-bottom: 0;\n}\n\n.mj-timeline__segment-header {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 16px;\n margin-bottom: var(--mj-timeline-gap);\n background: var(--mj-timeline-segment-bg);\n border-radius: 4px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-timeline-segment-text);\n user-select: none;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);\n position: relative;\n z-index: 10;\n}\n\n.mj-timeline__segment-header--clickable {\n cursor: pointer;\n transition: all var(--mj-timeline-transition);\n}\n\n.mj-timeline__segment-header--clickable:hover {\n filter: brightness(1.1);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);\n}\n\n.mj-timeline__segment-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n color: inherit;\n font-size: 10px;\n opacity: 0.9;\n transition: transform var(--mj-timeline-transition);\n}\n\n.mj-timeline__segment--collapsed .mj-timeline__segment-toggle {\n transform: rotate(-90deg);\n}\n\n.mj-timeline__segment-label {\n font-size: inherit;\n font-weight: inherit;\n color: inherit;\n}\n\n.mj-timeline__segment-count {\n display: none;\n}\n\n.mj-timeline__segment-line {\n display: none;\n}\n\n.mj-timeline__segment-content {\n overflow: visible;\n transition: max-height 0.25s ease, opacity 0.2s ease;\n padding-top: 8px;\n}\n\n.mj-timeline__segment-content--hidden {\n max-height: 0;\n opacity: 0;\n pointer-events: none;\n}\n\n/* ============================================================================\n TIMELINE AXIS - Vertical line in center\n ============================================================================ */\n\n.mj-timeline__axis {\n position: relative;\n padding-left: 0;\n}\n\n/* Vertical center line */\n.mj-timeline__axis::before {\n content: '';\n position: absolute;\n left: 50%;\n top: 0;\n bottom: 0;\n width: var(--mj-timeline-line-width);\n background: var(--mj-timeline-line-color);\n transform: translateX(-50%);\n}\n\n/* ============================================================================\n TIMELINE EVENT - Alternating left/right by default\n ============================================================================ */\n\n.mj-timeline__event {\n position: relative;\n display: flex;\n align-items: flex-start;\n margin-bottom: var(--mj-timeline-gap);\n width: 100%;\n}\n\n/* Even events (0, 2, 4...) - card on LEFT, date on RIGHT */\n.mj-timeline__event {\n padding-right: calc(50% + 32px);\n justify-content: flex-end;\n}\n\n/* Odd events (1, 3, 5...) - card on RIGHT, date on LEFT */\n.mj-timeline__event--odd {\n padding-right: 0;\n padding-left: calc(50% + 32px);\n justify-content: flex-start;\n flex-direction: row;\n}\n\n.mj-timeline__event:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n MARKER - Circular dot on the axis\n ============================================================================ */\n\n.mj-timeline__marker {\n position: absolute;\n left: 50%;\n top: 12px;\n transform: translateX(-50%);\n width: var(--mj-timeline-marker-size);\n height: var(--mj-timeline-marker-size);\n background: var(--mj-timeline-marker-fill);\n border: 2px solid var(--mj-timeline-marker-bg);\n border-radius: 50%;\n box-shadow: 0 0 0 2px var(--mj-timeline-marker-fill);\n z-index: 5;\n transition: transform var(--mj-timeline-transition);\n}\n\n.mj-timeline__marker-icon {\n display: none;\n}\n\n.mj-timeline__event:hover .mj-timeline__marker {\n transform: translateX(-50%) scale(1.2);\n}\n\n.mj-timeline__connector {\n display: none;\n}\n\n/* ============================================================================\n DATE LABEL - Positioned opposite the card\n ============================================================================ */\n\n.mj-timeline__date-label {\n position: absolute;\n top: 10px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-timeline-text-secondary);\n white-space: nowrap;\n}\n\n/* Even events - date on RIGHT side of axis */\n.mj-timeline__event:not(.mj-timeline__event--odd) .mj-timeline__date-label {\n left: calc(50% + 24px);\n text-align: left;\n}\n\n/* Odd events - date on LEFT side of axis */\n.mj-timeline__event--odd .mj-timeline__date-label {\n right: calc(50% + 24px);\n text-align: right;\n}\n\n/* ============================================================================\n EVENT CARD - Clean design matching Kendo\n ============================================================================ */\n\n.mj-timeline__card {\n background: var(--mj-timeline-card-bg);\n border: 1px solid var(--mj-timeline-card-border);\n border-radius: var(--mj-timeline-card-radius);\n box-shadow: var(--mj-timeline-card-shadow);\n overflow: hidden;\n transition: all var(--mj-timeline-transition);\n cursor: pointer;\n max-width: var(--mj-timeline-card-max-width);\n min-width: var(--mj-timeline-card-min-width);\n width: 100%;\n}\n\n/* Card arrow/pointer toward the axis */\n.mj-timeline__card::before {\n content: '';\n position: absolute;\n top: 14px;\n width: 10px;\n height: 10px;\n background: var(--mj-timeline-card-bg);\n border: 1px solid var(--mj-timeline-card-border);\n transform: rotate(45deg);\n}\n\n/* Even events - arrow points RIGHT toward axis */\n.mj-timeline__event:not(.mj-timeline__event--odd) .mj-timeline__card::before {\n right: -6px;\n border-left: none;\n border-bottom: none;\n}\n\n/* Odd events - arrow points LEFT toward axis */\n.mj-timeline__event--odd .mj-timeline__card::before {\n left: -6px;\n border-right: none;\n border-top: none;\n}\n\n/* Hover state */\n.mj-timeline__card:hover {\n box-shadow: var(--mj-timeline-card-shadow-hover);\n border-color: var(--mj-timeline-accent);\n}\n\n/* Selected/Focused state - prominent highlight with animation */\n.mj-timeline__event--focused .mj-timeline__card {\n background: var(--mj-timeline-accent-light);\n border-color: var(--mj-timeline-accent);\n box-shadow:\n 0 0 0 3px var(--mj-timeline-accent),\n 0 8px 24px rgba(70, 120, 168, 0.25);\n transform: scale(1.02);\n}\n\n/* Selected marker - larger and more prominent */\n.mj-timeline__event--focused .mj-timeline__marker {\n transform: translateX(-50%) scale(1.4);\n box-shadow:\n 0 0 0 3px var(--mj-timeline-marker-fill),\n 0 0 12px rgba(70, 120, 168, 0.5);\n}\n\n/* Pulse animation for selected marker */\n.mj-timeline__event--focused .mj-timeline__marker::after {\n content: '';\n position: absolute;\n top: -4px;\n left: -4px;\n right: -4px;\n bottom: -4px;\n border-radius: 50%;\n border: 2px solid var(--mj-timeline-accent);\n animation: mj-timeline-pulse 1.5s ease-out infinite;\n}\n\n@keyframes mj-timeline-pulse {\n 0% {\n transform: scale(1);\n opacity: 0.8;\n }\n 100% {\n transform: scale(1.8);\n opacity: 0;\n }\n}\n\n/* Selected date label - bolder */\n.mj-timeline__event--focused .mj-timeline__date-label {\n color: var(--mj-timeline-accent);\n font-weight: 600;\n}\n\n/* Card arrow gets accent color when selected */\n.mj-timeline__event--focused .mj-timeline__card::before {\n border-color: var(--mj-timeline-accent);\n background: var(--mj-timeline-accent-light);\n}\n\n/* ============================================================================\n CARD HEADER\n ============================================================================ */\n\n.mj-timeline__card-header {\n display: flex;\n align-items: flex-start;\n padding: var(--mj-timeline-card-padding);\n gap: 10px;\n}\n\n.mj-timeline__card-header-content {\n flex: 1;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n}\n\n.mj-timeline__card-icon {\n display: none;\n}\n\n.mj-timeline__card-titles {\n flex: 1;\n min-width: 0;\n}\n\n.mj-timeline__card-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-timeline-text-primary);\n line-height: 1.35;\n}\n\n.mj-timeline__card-subtitle {\n display: block;\n font-size: 13px;\n color: var(--mj-timeline-text-secondary);\n margin-top: 4px;\n}\n\n.mj-timeline__card-date {\n display: block;\n font-size: 12px;\n color: var(--mj-timeline-text-muted);\n margin-top: 6px;\n}\n\n.mj-timeline__card-date::before {\n display: none;\n}\n\n.mj-timeline__card-toggle {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: var(--mj-timeline-text-muted);\n border-radius: 4px;\n cursor: pointer;\n transition: all var(--mj-timeline-transition);\n font-size: 14px;\n}\n\n.mj-timeline__card-toggle:hover {\n background: var(--mj-timeline-accent-light);\n color: var(--mj-timeline-accent);\n}\n\n/* ============================================================================\n CARD IMAGE\n ============================================================================ */\n\n.mj-timeline__card-image {\n overflow: hidden;\n border-radius: 4px;\n flex-shrink: 0;\n}\n\n.mj-timeline__card-image img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.mj-timeline__card-image--left.mj-timeline__card-image--small {\n width: 48px;\n height: 48px;\n}\n\n.mj-timeline__card-image--left.mj-timeline__card-image--medium {\n width: 72px;\n height: 72px;\n}\n\n.mj-timeline__card-image--left.mj-timeline__card-image--large {\n width: 96px;\n height: 96px;\n}\n\n.mj-timeline__card-image--top {\n width: calc(100% - 32px);\n max-height: 180px;\n margin: 0 16px 12px;\n}\n\n/* ============================================================================\n CARD BODY\n ============================================================================ */\n\n.mj-timeline__card-body {\n padding: 0 var(--mj-timeline-card-padding) var(--mj-timeline-card-padding);\n}\n\n.mj-timeline__card-body--collapsed {\n display: none;\n}\n\n.mj-timeline__card-description {\n font-size: 13px;\n line-height: 1.6;\n color: var(--mj-timeline-text-secondary);\n margin-bottom: 12px;\n}\n\n.mj-timeline__card-description--clamped {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.mj-timeline__card-description:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n CARD FIELDS\n ============================================================================ */\n\n.mj-timeline__card-fields {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 12px;\n}\n\n.mj-timeline__card-fields--summary {\n padding-top: 12px;\n border-top: 1px solid var(--mj-timeline-line-color);\n margin-top: 12px;\n}\n\n.mj-timeline__card-fields--expanded {\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--mj-timeline-line-color);\n}\n\n.mj-timeline__card-field {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 12px;\n}\n\n.mj-timeline__card-field-icon {\n color: var(--mj-timeline-text-muted);\n font-size: 10px;\n}\n\n.mj-timeline__card-field-label {\n color: var(--mj-timeline-text-muted);\n}\n\n.mj-timeline__card-field-value {\n color: var(--mj-timeline-text-secondary);\n font-weight: 500;\n}\n\n/* ============================================================================\n CARD ACTIONS\n ============================================================================ */\n\n.mj-timeline__card-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 0 var(--mj-timeline-card-padding) var(--mj-timeline-card-padding);\n justify-content: flex-end;\n}\n\n.mj-timeline__card-actions--hover-only {\n opacity: 0;\n transition: opacity var(--mj-timeline-transition);\n}\n\n.mj-timeline__card:hover .mj-timeline__card-actions--hover-only,\n.mj-timeline__card:focus-within .mj-timeline__card-actions--hover-only {\n opacity: 1;\n}\n\n.mj-timeline__action {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 6px 12px;\n font-size: 12px;\n font-weight: 500;\n border: 1px solid;\n border-radius: 4px;\n cursor: pointer;\n transition: all var(--mj-timeline-transition);\n}\n\n.mj-timeline__action:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.mj-timeline__action i {\n font-size: 10px;\n}\n\n.mj-timeline__action--primary {\n background: var(--mj-timeline-accent);\n border-color: var(--mj-timeline-accent);\n color: #ffffff;\n}\n\n.mj-timeline__action--primary:hover:not(:disabled) {\n filter: brightness(1.1);\n}\n\n.mj-timeline__action--secondary {\n background: transparent;\n border-color: var(--mj-timeline-card-border);\n color: var(--mj-timeline-text-primary);\n}\n\n.mj-timeline__action--secondary:hover:not(:disabled) {\n background: var(--mj-timeline-accent-light);\n border-color: var(--mj-timeline-accent);\n color: var(--mj-timeline-accent);\n}\n\n.mj-timeline__action--danger {\n background: #cf222e;\n border-color: #cf222e;\n color: #ffffff;\n}\n\n.mj-timeline__action--link {\n background: transparent;\n border-color: transparent;\n color: var(--mj-timeline-accent);\n}\n\n.mj-timeline__action--link:hover:not(:disabled) {\n text-decoration: underline;\n}\n\n/* ============================================================================\n VIRTUAL SCROLL\n ============================================================================ */\n\n.mj-timeline-scroll-sentinel {\n height: 1px;\n width: 100%;\n visibility: hidden;\n}\n\n/* ============================================================================\n SINGLE LAYOUT - Cards only on right side\n ============================================================================ */\n\n.mj-timeline--single .mj-timeline__axis::before {\n left: 24px;\n transform: none;\n}\n\n.mj-timeline--single .mj-timeline__segment-header {\n margin-left: 12px;\n}\n\n.mj-timeline--single .mj-timeline__event {\n padding-right: 0;\n padding-left: 56px;\n justify-content: flex-start;\n}\n\n.mj-timeline--single .mj-timeline__event--odd {\n padding-left: 56px;\n flex-direction: row;\n}\n\n.mj-timeline--single .mj-timeline__marker {\n left: 24px;\n transform: translateX(-50%);\n}\n\n.mj-timeline--single .mj-timeline__date-label {\n display: none;\n}\n\n.mj-timeline--single .mj-timeline__card::before {\n left: -6px;\n right: auto;\n border-right: none;\n border-top: none;\n border-left: 1px solid var(--mj-timeline-card-border);\n border-bottom: 1px solid var(--mj-timeline-card-border);\n}\n\n.mj-timeline--single .mj-timeline__card {\n max-width: none;\n}\n\n/* ============================================================================\n ALTERNATING LAYOUT - Explicit class (same as default vertical)\n ============================================================================ */\n\n.mj-timeline--alternating .mj-timeline__segment-header {\n left: 50%;\n transform: translateX(-50%);\n}\n\n/* ============================================================================\n HORIZONTAL LAYOUT\n ============================================================================ */\n\n.mj-timeline--horizontal {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n align-items: flex-start;\n overflow-x: auto;\n overflow-y: hidden;\n padding: 80px 40px 24px;\n min-width: 100%;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment {\n flex: 0 0 auto;\n display: flex;\n flex-direction: column;\n margin-bottom: 0;\n margin-right: 40px;\n position: relative;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment:last-child {\n margin-right: 0;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment-header {\n position: absolute;\n top: -56px;\n left: 0;\n margin-bottom: 0;\n white-space: nowrap;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment-content {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n}\n\n.mj-timeline--horizontal .mj-timeline__axis {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n padding-left: 0;\n min-width: max-content;\n gap: 0;\n}\n\n/* Horizontal axis line */\n.mj-timeline--horizontal .mj-timeline__axis::before {\n left: 0;\n right: 0;\n top: 0;\n bottom: auto;\n width: 100%;\n height: var(--mj-timeline-line-width);\n transform: none;\n}\n\n.mj-timeline--horizontal .mj-timeline__event {\n flex-direction: column;\n align-items: center;\n margin-bottom: 0;\n margin-right: 48px;\n padding: 0;\n padding-top: 32px;\n min-width: 180px;\n max-width: 280px;\n width: auto;\n}\n\n.mj-timeline--horizontal .mj-timeline__event--odd {\n padding-left: 0;\n flex-direction: column;\n}\n\n.mj-timeline--horizontal .mj-timeline__event:last-child {\n margin-right: 0;\n}\n\n.mj-timeline--horizontal .mj-timeline__marker {\n position: absolute;\n left: 50%;\n /* Position so marker is centered on the axis line (top: 0 of axis) */\n /* Event has padding-top: 32px, marker is 14px, so top: -(32 + 7) = -39px for center */\n top: calc(-32px - var(--mj-timeline-marker-size) / 2);\n transform: translateX(-50%);\n margin: 0;\n}\n\n.mj-timeline--horizontal .mj-timeline__event:hover .mj-timeline__marker {\n transform: translateX(-50%) scale(1.2);\n}\n\n.mj-timeline--horizontal .mj-timeline__date-label {\n position: absolute;\n top: -40px;\n left: 50%;\n right: auto;\n transform: translateX(-50%);\n text-align: center;\n font-size: 12px;\n}\n\n.mj-timeline--horizontal .mj-timeline__card {\n max-width: none;\n width: 100%;\n min-width: 0;\n}\n\n.mj-timeline--horizontal .mj-timeline__card::before {\n display: none;\n}\n\n/* Horizontal focused marker override */\n.mj-timeline--horizontal .mj-timeline__event--focused .mj-timeline__marker {\n transform: translateX(-50%) scale(1.4);\n}\n\n/* Horizontal collapsed segment - rotate 90\u00B0 CCW and compress */\n.mj-timeline--horizontal .mj-timeline__segment--collapsed {\n flex: 0 0 auto;\n width: 32px;\n min-width: 32px;\n margin-right: 4px;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment--collapsed .mj-timeline__segment-header {\n writing-mode: vertical-rl;\n text-orientation: mixed;\n transform: rotate(180deg);\n white-space: nowrap;\n padding: 8px 4px;\n margin: 0;\n top: 0;\n position: relative;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment--collapsed .mj-timeline__segment-content {\n display: none;\n}\n\n/* ============================================================================\n RESPONSIVE\n ============================================================================ */\n\n@media (max-width: 900px) {\n /* Force single layout on smaller screens */\n .mj-timeline--alternating .mj-timeline__axis::before {\n left: 24px;\n transform: none;\n }\n\n .mj-timeline--alternating .mj-timeline__segment-header {\n left: auto;\n transform: none;\n margin-left: 12px;\n }\n\n .mj-timeline--alternating .mj-timeline__event {\n padding-right: 0;\n padding-left: 56px;\n justify-content: flex-start;\n }\n\n .mj-timeline--alternating .mj-timeline__event--odd {\n padding-left: 56px;\n padding-right: 0;\n flex-direction: row;\n }\n\n .mj-timeline--alternating .mj-timeline__marker {\n left: 24px;\n transform: translateX(-50%);\n }\n\n .mj-timeline--alternating .mj-timeline__event:hover .mj-timeline__marker {\n transform: translateX(-50%) scale(1.2);\n }\n\n .mj-timeline--alternating .mj-timeline__date-label {\n display: none;\n }\n\n .mj-timeline--alternating .mj-timeline__card::before {\n left: -6px;\n right: auto;\n border-right: none;\n border-top: none;\n border-left: 1px solid var(--mj-timeline-card-border);\n border-bottom: 1px solid var(--mj-timeline-card-border);\n }\n\n .mj-timeline--alternating .mj-timeline__card {\n max-width: none;\n }\n}\n\n@media (max-width: 600px) {\n mj-timeline {\n --mj-timeline-card-padding: 12px;\n --mj-timeline-gap: 16px;\n --mj-timeline-marker-size: 12px;\n }\n\n .mj-timeline {\n padding: 16px 12px;\n }\n\n .mj-timeline__card-title {\n font-size: 14px;\n }\n\n .mj-timeline--horizontal .mj-timeline__event {\n min-width: 160px;\n max-width: 220px;\n margin-right: 32px;\n }\n}\n\n/* Touch devices */\n@media (hover: none) and (pointer: coarse) {\n .mj-timeline__card-actions--hover-only {\n opacity: 1;\n }\n\n .mj-timeline__action {\n min-height: 40px;\n }\n}\n\n/* Reduced motion */\n@media (prefers-reduced-motion: reduce) {\n .mj-timeline__card,\n .mj-timeline__marker,\n .mj-timeline__segment-toggle {\n transition: none;\n }\n}\n"], encapsulation: 2, changeDetection: 0 });
|
|
188
2003
|
}
|
|
189
2004
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TimelineComponent, [{
|
|
190
2005
|
type: Component,
|
|
191
|
-
args: [{ selector: 'mj-timeline', template: "<div class=\"wrapper\"> \n <kendo-timeline\n [events]=\"events\"\n [orientation]=\"DisplayOrientation\"\n [collapsibleEvents]=\"true\"\n [alterMode]=\"true\"\n >\n </kendo-timeline>\n</div> " }]
|
|
192
|
-
}],
|
|
2006
|
+
args: [{ selector: 'mj-timeline', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<!--\n MJ Timeline Component Template\n\n Structure:\n - Loading state\n - Empty state\n - Timeline container (vertical/horizontal)\n - Time segments (collapsible)\n - Segment header\n - Event cards within segment\n - Or flat list of events (when segmentGrouping='none')\n - Virtual scroll sentinel\n-->\n\n<div\n class=\"mj-timeline\"\n [class.mj-timeline--vertical]=\"orientation === 'vertical'\"\n [class.mj-timeline--horizontal]=\"orientation === 'horizontal'\"\n [class.mj-timeline--single]=\"layout === 'single'\"\n [class.mj-timeline--alternating]=\"layout === 'alternating'\"\n [attr.aria-label]=\"ariaLabel\"\n role=\"list\"\n tabindex=\"0\"\n (keydown)=\"onKeyDown($event)\"\n #scrollContainer\n (scroll)=\"onScroll($event)\">\n\n <!-- Loading State -->\n <ng-container *ngIf=\"isLoading && !isInitialized\">\n <ng-container *ngIf=\"loadingTemplate; else defaultLoading\">\n <ng-container *ngTemplateOutlet=\"loadingTemplate\"></ng-container>\n </ng-container>\n <ng-template #defaultLoading>\n <div class=\"mj-timeline__loading\">\n <div class=\"mj-timeline__loading-spinner\"></div>\n <span class=\"mj-timeline__loading-text\">{{ loadingMessage }}</span>\n </div>\n </ng-template>\n </ng-container>\n\n <!-- Empty State -->\n <ng-container *ngIf=\"isInitialized && allEvents.length === 0 && !isLoading\">\n <ng-container *ngIf=\"emptyTemplate; else defaultEmpty\">\n <ng-container *ngTemplateOutlet=\"emptyTemplate\"></ng-container>\n </ng-container>\n <ng-template #defaultEmpty>\n <div class=\"mj-timeline__empty\">\n <i [class]=\"emptyIcon\" class=\"mj-timeline__empty-icon\"></i>\n <span class=\"mj-timeline__empty-text\">{{ emptyMessage }}</span>\n </div>\n </ng-template>\n </ng-container>\n\n <!-- Timeline Content -->\n <ng-container *ngIf=\"isInitialized && allEvents.length > 0\">\n\n <!-- Segmented View -->\n <ng-container *ngIf=\"segmentGrouping !== 'none'\">\n <div\n *ngFor=\"let segment of segments; trackBy: trackBySegmentLabel; let segmentIndex = index\"\n class=\"mj-timeline__segment\"\n [class.mj-timeline__segment--collapsed]=\"!segment.isExpanded\"\n [attr.data-segment-label]=\"segment.label\">\n\n <!-- Segment Header -->\n <div\n class=\"mj-timeline__segment-header\"\n [class.mj-timeline__segment-header--clickable]=\"segmentsCollapsible\"\n (click)=\"onSegmentClick(segment)\"\n role=\"button\"\n [attr.aria-expanded]=\"segment.isExpanded\"\n [attr.aria-controls]=\"'segment-content-' + segmentIndex\">\n\n <ng-container *ngIf=\"segmentHeaderTemplate; else defaultSegmentHeader\">\n <ng-container *ngTemplateOutlet=\"segmentHeaderTemplate; context: { segment: segment }\"></ng-container>\n </ng-container>\n\n <ng-template #defaultSegmentHeader>\n <span class=\"mj-timeline__segment-toggle\" *ngIf=\"segmentsCollapsible\">\n <i [class]=\"segment.isExpanded ? 'fa-solid fa-chevron-down' : 'fa-solid fa-chevron-right'\"></i>\n </span>\n <span class=\"mj-timeline__segment-label\">{{ segment.label }}</span>\n <span class=\"mj-timeline__segment-count\">({{ segment.eventCount }} {{ segment.eventCount === 1 ? 'event' : 'events' }})</span>\n <span class=\"mj-timeline__segment-line\"></span>\n </ng-template>\n </div>\n\n <!-- Segment Content -->\n <div\n class=\"mj-timeline__segment-content\"\n [id]=\"'segment-content-' + segmentIndex\"\n [class.mj-timeline__segment-content--hidden]=\"!segment.isExpanded\">\n\n <div class=\"mj-timeline__axis\">\n <!-- Events in Segment -->\n <ng-container *ngFor=\"let event of segment.events; trackBy: trackByEventId; let eventIndex = index; let isOdd = odd\">\n <ng-container *ngTemplateOutlet=\"eventCard; context: {\n event: event,\n index: eventIndex,\n isOdd: isOdd,\n globalIndex: getGlobalIndex(event)\n }\"></ng-container>\n </ng-container>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Flat View (no segments) -->\n <ng-container *ngIf=\"segmentGrouping === 'none'\">\n <div class=\"mj-timeline__axis\">\n <ng-container *ngFor=\"let event of allEvents; trackBy: trackByEventId; let eventIndex = index; let isOdd = odd\">\n <ng-container *ngTemplateOutlet=\"eventCard; context: {\n event: event,\n index: eventIndex,\n isOdd: isOdd,\n globalIndex: eventIndex\n }\"></ng-container>\n </ng-container>\n </div>\n </ng-container>\n\n </ng-container>\n\n <!-- Virtual Scroll Sentinel -->\n <div\n class=\"mj-timeline-scroll-sentinel\"\n *ngIf=\"virtualScroll.enabled && scrollState.hasMore\">\n </div>\n\n <!-- Loading More Indicator -->\n <div\n class=\"mj-timeline__loading-more\"\n *ngIf=\"scrollState.isLoading && virtualScroll.showLoadingIndicator\">\n <div class=\"mj-timeline__loading-spinner mj-timeline__loading-spinner--small\"></div>\n <span>{{ virtualScroll.loadingMessage }}</span>\n </div>\n\n</div>\n\n<!-- Event Card Template -->\n<ng-template #eventCard let-event=\"event\" let-index=\"index\" let-isOdd=\"isOdd\" let-globalIndex=\"globalIndex\">\n <div\n class=\"mj-timeline__event\"\n [class.mj-timeline__event--odd]=\"isOdd && layout === 'alternating'\"\n [class.mj-timeline__event--even]=\"!isOdd && layout === 'alternating'\"\n [class.mj-timeline__event--expanded]=\"event.isExpanded\"\n [class.mj-timeline__event--focused]=\"isEventSelected(event, globalIndex)\"\n [attr.data-event-id]=\"event.id\"\n role=\"listitem\"\n [attr.aria-expanded]=\"event.isExpanded\">\n\n <!-- Timeline Marker -->\n <div class=\"mj-timeline__marker\" [style.background-color]=\"getColor(event)\">\n <i [class]=\"getIcon(event)\" class=\"mj-timeline__marker-icon\"></i>\n </div>\n\n <!-- Connector Line -->\n <div class=\"mj-timeline__connector\" [style.background-color]=\"getColor(event)\"></div>\n\n <!-- Date Label (for alternating layout) -->\n <div class=\"mj-timeline__date-label\" *ngIf=\"layout === 'alternating'\">\n {{ formatDate(event.date) }}\n </div>\n\n <!-- Event Card -->\n <div\n class=\"mj-timeline__card\"\n [ngClass]=\"getEffectiveCardConfig(event).cssClass\"\n [style.max-width]=\"getEffectiveCardConfig(event).maxWidth\"\n [style.min-width]=\"getEffectiveCardConfig(event).minWidth\"\n [style.border-left-color]=\"getColor(event)\"\n (click)=\"onEventClick(event, globalIndex, $event)\"\n (mouseenter)=\"onEventMouseEnter(event, globalIndex, $event)\"\n (mouseleave)=\"onEventMouseLeave(event, globalIndex, $event)\">\n\n <!-- Custom Card Template -->\n <ng-container *ngIf=\"cardTemplate; else defaultCard\">\n <ng-container *ngTemplateOutlet=\"cardTemplate; context: {\n event: event,\n group: groups[event.groupIndex]\n }\"></ng-container>\n </ng-container>\n\n <!-- Default Card Template -->\n <ng-template #defaultCard>\n <!-- Card Header -->\n <div class=\"mj-timeline__card-header\">\n <!-- Image (left position) -->\n <div\n class=\"mj-timeline__card-image mj-timeline__card-image--left\"\n *ngIf=\"event.imageUrl && getEffectiveCardConfig(event).imagePosition === 'left'\"\n [class.mj-timeline__card-image--small]=\"getEffectiveCardConfig(event).imageSize === 'small'\"\n [class.mj-timeline__card-image--medium]=\"getEffectiveCardConfig(event).imageSize === 'medium'\"\n [class.mj-timeline__card-image--large]=\"getEffectiveCardConfig(event).imageSize === 'large'\">\n <img [src]=\"event.imageUrl\" [alt]=\"event.title\" />\n </div>\n\n <div class=\"mj-timeline__card-header-content\">\n <!-- Custom Header Template -->\n <ng-container *ngIf=\"headerTemplate; else defaultHeader\">\n <ng-container *ngTemplateOutlet=\"headerTemplate; context: { event: event }\"></ng-container>\n </ng-container>\n\n <ng-template #defaultHeader>\n <!-- Icon -->\n <span\n class=\"mj-timeline__card-icon\"\n *ngIf=\"getEffectiveCardConfig(event).showIcon\"\n [style.color]=\"getColor(event)\">\n <i [class]=\"getIcon(event)\"></i>\n </span>\n\n <!-- Title & Subtitle -->\n <div class=\"mj-timeline__card-titles\">\n <h4 class=\"mj-timeline__card-title\">{{ event.title }}</h4>\n <span\n class=\"mj-timeline__card-subtitle\"\n *ngIf=\"event.subtitle && getEffectiveCardConfig(event).showSubtitle\">\n {{ event.subtitle }}\n </span>\n <span\n class=\"mj-timeline__card-date\"\n *ngIf=\"getEffectiveCardConfig(event).showDate && layout !== 'alternating'\">\n {{ formatDate(event.date, getEffectiveCardConfig(event).dateFormat) }}\n </span>\n </div>\n\n <!-- Expand/Collapse Toggle -->\n <button\n class=\"mj-timeline__card-toggle\"\n *ngIf=\"getEffectiveCardConfig(event).collapsible\"\n (click)=\"onToggleExpand(event, globalIndex, $event)\"\n [attr.aria-label]=\"event.isExpanded ? 'Collapse' : 'Expand'\"\n type=\"button\">\n <i [class]=\"event.isExpanded ? 'fa-solid fa-chevron-up' : 'fa-solid fa-chevron-down'\"></i>\n </button>\n </ng-template>\n </div>\n </div>\n\n <!-- Image (top position) -->\n <div\n class=\"mj-timeline__card-image mj-timeline__card-image--top\"\n *ngIf=\"event.imageUrl && getEffectiveCardConfig(event).imagePosition === 'top'\">\n <img [src]=\"event.imageUrl\" [alt]=\"event.title\" />\n </div>\n\n <!-- Card Body -->\n <div\n class=\"mj-timeline__card-body\"\n [class.mj-timeline__card-body--collapsed]=\"!event.isExpanded && getEffectiveCardConfig(event).collapsible\">\n\n <!-- Custom Body Template -->\n <ng-container *ngIf=\"bodyTemplate; else defaultBody\">\n <ng-container *ngTemplateOutlet=\"bodyTemplate; context: { event: event }\"></ng-container>\n </ng-container>\n\n <ng-template #defaultBody>\n <!-- Summary Fields (always visible) -->\n <div\n class=\"mj-timeline__card-fields mj-timeline__card-fields--summary\"\n *ngIf=\"getEffectiveCardConfig(event).summaryFields?.length\">\n <ng-container *ngFor=\"let field of getEffectiveCardConfig(event).summaryFields\">\n <div class=\"mj-timeline__card-field\" [ngClass]=\"field.cssClass\">\n <i *ngIf=\"field.icon\" [ngClass]=\"field.icon\" class=\"mj-timeline__card-field-icon\"></i>\n <span *ngIf=\"!field.hideLabel && field.label\" class=\"mj-timeline__card-field-label\">{{ field.label }}:</span>\n <span class=\"mj-timeline__card-field-value\">{{ getFieldValue(event, field) }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Description (expanded view) -->\n <div\n class=\"mj-timeline__card-description\"\n *ngIf=\"event.description && event.isExpanded\"\n [class.mj-timeline__card-description--clamped]=\"(getEffectiveCardConfig(event).descriptionMaxLines ?? 0) > 0\"\n [style.-webkit-line-clamp]=\"getEffectiveCardConfig(event).descriptionMaxLines || null\">\n <ng-container *ngIf=\"getEffectiveCardConfig(event).allowHtmlDescription; else plainDescription\">\n <div [innerHTML]=\"event.description\"></div>\n </ng-container>\n <ng-template #plainDescription>\n {{ event.description }}\n </ng-template>\n </div>\n\n <!-- Expanded Fields -->\n <div\n class=\"mj-timeline__card-fields mj-timeline__card-fields--expanded\"\n *ngIf=\"event.isExpanded && getEffectiveCardConfig(event).expandedFields?.length\">\n <ng-container *ngFor=\"let field of getEffectiveCardConfig(event).expandedFields\">\n <div class=\"mj-timeline__card-field\" [ngClass]=\"field.cssClass\">\n <i *ngIf=\"field.icon\" [ngClass]=\"field.icon\" class=\"mj-timeline__card-field-icon\"></i>\n <span *ngIf=\"!field.hideLabel\" class=\"mj-timeline__card-field-label\">{{ field.label || field.fieldName }}:</span>\n <span class=\"mj-timeline__card-field-value\">{{ getFieldValue(event, field) }}</span>\n </div>\n </ng-container>\n </div>\n </ng-template>\n </div>\n\n <!-- Card Actions -->\n <div\n class=\"mj-timeline__card-actions\"\n [class.mj-timeline__card-actions--hover-only]=\"getEffectiveCardConfig(event).actionsOnHover\"\n *ngIf=\"getActions(event).length > 0\">\n\n <!-- Custom Actions Template -->\n <ng-container *ngIf=\"actionsTemplate; else defaultActions\">\n <ng-container *ngTemplateOutlet=\"actionsTemplate; context: {\n event: event,\n actions: getActions(event)\n }\"></ng-container>\n </ng-container>\n\n <ng-template #defaultActions>\n <button\n *ngFor=\"let action of getActions(event)\"\n class=\"mj-timeline__action\"\n [class.mj-timeline__action--primary]=\"action.variant === 'primary'\"\n [class.mj-timeline__action--secondary]=\"action.variant === 'secondary' || !action.variant\"\n [class.mj-timeline__action--danger]=\"action.variant === 'danger'\"\n [class.mj-timeline__action--link]=\"action.variant === 'link'\"\n [ngClass]=\"action.cssClass\"\n [disabled]=\"action.disabled\"\n [title]=\"action.tooltip || ''\"\n (click)=\"onActionClick(event, action, globalIndex, $event)\"\n type=\"button\">\n <i *ngIf=\"action.icon\" [ngClass]=\"action.icon\"></i>\n <span>{{ action.label }}</span>\n </button>\n </ng-template>\n </div>\n </ng-template>\n </div>\n </div>\n</ng-template>\n", styles: ["/**\n * MJ Timeline Component Styles\n * Kendo-inspired design with vertical alternating and horizontal layouts\n */\n\n/* ============================================================================\n CSS VARIABLES (Theming)\n ============================================================================ */\n\nmj-timeline {\n /* Colors */\n --mj-timeline-bg: transparent;\n --mj-timeline-line-color: #d0d7de;\n --mj-timeline-marker-bg: #ffffff;\n --mj-timeline-marker-border: #4678a8;\n --mj-timeline-marker-fill: #4678a8;\n --mj-timeline-card-bg: #ffffff;\n --mj-timeline-card-border: #d0d7de;\n --mj-timeline-card-shadow: 0 1px 3px rgba(31, 35, 40, 0.08);\n --mj-timeline-card-shadow-hover: 0 4px 12px rgba(31, 35, 40, 0.12);\n --mj-timeline-card-radius: 6px;\n --mj-timeline-text-primary: #1f2328;\n --mj-timeline-text-secondary: #656d76;\n --mj-timeline-text-muted: #8c959f;\n --mj-timeline-accent: #4678a8;\n --mj-timeline-accent-light: rgba(70, 120, 168, 0.08);\n --mj-timeline-segment-bg: #4678a8;\n --mj-timeline-segment-text: #ffffff;\n --mj-timeline-focus-ring: 0 0 0 2px rgba(70, 120, 168, 0.3);\n\n /* Sizing */\n --mj-timeline-line-width: 2px;\n --mj-timeline-marker-size: 14px;\n --mj-timeline-card-padding: 16px;\n --mj-timeline-card-max-width: 400px;\n --mj-timeline-card-min-width: 200px;\n --mj-timeline-gap: 24px;\n --mj-timeline-segment-gap: 16px;\n --mj-timeline-axis-offset: 50%;\n\n /* Animation */\n --mj-timeline-transition: 0.15s ease;\n\n display: block;\n width: 100%;\n height: 100%;\n}\n\n/* Dark mode */\n.dark-theme mj-timeline,\n[data-theme=\"dark\"] mj-timeline {\n --mj-timeline-line-color: #3d444d;\n --mj-timeline-card-bg: #161b22;\n --mj-timeline-card-border: #3d444d;\n --mj-timeline-card-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n --mj-timeline-text-primary: #e6edf3;\n --mj-timeline-text-secondary: #8b949e;\n --mj-timeline-text-muted: #6e7681;\n --mj-timeline-accent: #58a6ff;\n --mj-timeline-accent-light: rgba(88, 166, 255, 0.1);\n --mj-timeline-marker-border: #58a6ff;\n --mj-timeline-marker-fill: #58a6ff;\n --mj-timeline-segment-bg: #58a6ff;\n}\n\n/* ============================================================================\n MAIN CONTAINER\n ============================================================================ */\n\n.mj-timeline {\n position: relative;\n width: 100%;\n height: 100%;\n padding: 0;\n background: var(--mj-timeline-bg);\n outline: none;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n/* Subtle scrollbar */\n.mj-timeline::-webkit-scrollbar {\n width: 6px;\n}\n\n.mj-timeline::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.mj-timeline::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.15);\n border-radius: 3px;\n}\n\n.mj-timeline::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.25);\n}\n\n.mj-timeline:focus-visible {\n box-shadow: inset var(--mj-timeline-focus-ring);\n}\n\n/* ============================================================================\n LOADING & EMPTY STATES\n ============================================================================ */\n\n.mj-timeline__loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n gap: 12px;\n}\n\n.mj-timeline__loading-spinner {\n width: 24px;\n height: 24px;\n border: 2px solid var(--mj-timeline-line-color);\n border-top-color: var(--mj-timeline-accent);\n border-radius: 50%;\n animation: mj-timeline-spin 0.6s linear infinite;\n}\n\n.mj-timeline__loading-spinner--small {\n width: 14px;\n height: 14px;\n}\n\n.mj-timeline__loading-text {\n color: var(--mj-timeline-text-muted);\n font-size: 13px;\n}\n\n.mj-timeline__loading-more {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n gap: 8px;\n color: var(--mj-timeline-text-muted);\n font-size: 12px;\n}\n\n@keyframes mj-timeline-spin {\n to { transform: rotate(360deg); }\n}\n\n.mj-timeline__empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 64px 24px;\n gap: 8px;\n text-align: center;\n}\n\n.mj-timeline__empty-icon {\n font-size: 32px;\n color: var(--mj-timeline-text-muted);\n opacity: 0.4;\n}\n\n.mj-timeline__empty-text {\n color: var(--mj-timeline-text-secondary);\n font-size: 13px;\n}\n\n/* ============================================================================\n TIME SEGMENTS - Year/Month badges on the axis\n ============================================================================ */\n\n.mj-timeline__segment {\n margin-bottom: var(--mj-timeline-segment-gap);\n position: relative;\n}\n\n.mj-timeline__segment:last-child {\n margin-bottom: 0;\n}\n\n.mj-timeline__segment-header {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 16px;\n margin-bottom: var(--mj-timeline-gap);\n background: var(--mj-timeline-segment-bg);\n border-radius: 4px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-timeline-segment-text);\n user-select: none;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);\n position: relative;\n z-index: 10;\n}\n\n.mj-timeline__segment-header--clickable {\n cursor: pointer;\n transition: all var(--mj-timeline-transition);\n}\n\n.mj-timeline__segment-header--clickable:hover {\n filter: brightness(1.1);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);\n}\n\n.mj-timeline__segment-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n color: inherit;\n font-size: 10px;\n opacity: 0.9;\n transition: transform var(--mj-timeline-transition);\n}\n\n.mj-timeline__segment--collapsed .mj-timeline__segment-toggle {\n transform: rotate(-90deg);\n}\n\n.mj-timeline__segment-label {\n font-size: inherit;\n font-weight: inherit;\n color: inherit;\n}\n\n.mj-timeline__segment-count {\n display: none;\n}\n\n.mj-timeline__segment-line {\n display: none;\n}\n\n.mj-timeline__segment-content {\n overflow: visible;\n transition: max-height 0.25s ease, opacity 0.2s ease;\n padding-top: 8px;\n}\n\n.mj-timeline__segment-content--hidden {\n max-height: 0;\n opacity: 0;\n pointer-events: none;\n}\n\n/* ============================================================================\n TIMELINE AXIS - Vertical line in center\n ============================================================================ */\n\n.mj-timeline__axis {\n position: relative;\n padding-left: 0;\n}\n\n/* Vertical center line */\n.mj-timeline__axis::before {\n content: '';\n position: absolute;\n left: 50%;\n top: 0;\n bottom: 0;\n width: var(--mj-timeline-line-width);\n background: var(--mj-timeline-line-color);\n transform: translateX(-50%);\n}\n\n/* ============================================================================\n TIMELINE EVENT - Alternating left/right by default\n ============================================================================ */\n\n.mj-timeline__event {\n position: relative;\n display: flex;\n align-items: flex-start;\n margin-bottom: var(--mj-timeline-gap);\n width: 100%;\n}\n\n/* Even events (0, 2, 4...) - card on LEFT, date on RIGHT */\n.mj-timeline__event {\n padding-right: calc(50% + 32px);\n justify-content: flex-end;\n}\n\n/* Odd events (1, 3, 5...) - card on RIGHT, date on LEFT */\n.mj-timeline__event--odd {\n padding-right: 0;\n padding-left: calc(50% + 32px);\n justify-content: flex-start;\n flex-direction: row;\n}\n\n.mj-timeline__event:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n MARKER - Circular dot on the axis\n ============================================================================ */\n\n.mj-timeline__marker {\n position: absolute;\n left: 50%;\n top: 12px;\n transform: translateX(-50%);\n width: var(--mj-timeline-marker-size);\n height: var(--mj-timeline-marker-size);\n background: var(--mj-timeline-marker-fill);\n border: 2px solid var(--mj-timeline-marker-bg);\n border-radius: 50%;\n box-shadow: 0 0 0 2px var(--mj-timeline-marker-fill);\n z-index: 5;\n transition: transform var(--mj-timeline-transition);\n}\n\n.mj-timeline__marker-icon {\n display: none;\n}\n\n.mj-timeline__event:hover .mj-timeline__marker {\n transform: translateX(-50%) scale(1.2);\n}\n\n.mj-timeline__connector {\n display: none;\n}\n\n/* ============================================================================\n DATE LABEL - Positioned opposite the card\n ============================================================================ */\n\n.mj-timeline__date-label {\n position: absolute;\n top: 10px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-timeline-text-secondary);\n white-space: nowrap;\n}\n\n/* Even events - date on RIGHT side of axis */\n.mj-timeline__event:not(.mj-timeline__event--odd) .mj-timeline__date-label {\n left: calc(50% + 24px);\n text-align: left;\n}\n\n/* Odd events - date on LEFT side of axis */\n.mj-timeline__event--odd .mj-timeline__date-label {\n right: calc(50% + 24px);\n text-align: right;\n}\n\n/* ============================================================================\n EVENT CARD - Clean design matching Kendo\n ============================================================================ */\n\n.mj-timeline__card {\n background: var(--mj-timeline-card-bg);\n border: 1px solid var(--mj-timeline-card-border);\n border-radius: var(--mj-timeline-card-radius);\n box-shadow: var(--mj-timeline-card-shadow);\n overflow: hidden;\n transition: all var(--mj-timeline-transition);\n cursor: pointer;\n max-width: var(--mj-timeline-card-max-width);\n min-width: var(--mj-timeline-card-min-width);\n width: 100%;\n}\n\n/* Card arrow/pointer toward the axis */\n.mj-timeline__card::before {\n content: '';\n position: absolute;\n top: 14px;\n width: 10px;\n height: 10px;\n background: var(--mj-timeline-card-bg);\n border: 1px solid var(--mj-timeline-card-border);\n transform: rotate(45deg);\n}\n\n/* Even events - arrow points RIGHT toward axis */\n.mj-timeline__event:not(.mj-timeline__event--odd) .mj-timeline__card::before {\n right: -6px;\n border-left: none;\n border-bottom: none;\n}\n\n/* Odd events - arrow points LEFT toward axis */\n.mj-timeline__event--odd .mj-timeline__card::before {\n left: -6px;\n border-right: none;\n border-top: none;\n}\n\n/* Hover state */\n.mj-timeline__card:hover {\n box-shadow: var(--mj-timeline-card-shadow-hover);\n border-color: var(--mj-timeline-accent);\n}\n\n/* Selected/Focused state - prominent highlight with animation */\n.mj-timeline__event--focused .mj-timeline__card {\n background: var(--mj-timeline-accent-light);\n border-color: var(--mj-timeline-accent);\n box-shadow:\n 0 0 0 3px var(--mj-timeline-accent),\n 0 8px 24px rgba(70, 120, 168, 0.25);\n transform: scale(1.02);\n}\n\n/* Selected marker - larger and more prominent */\n.mj-timeline__event--focused .mj-timeline__marker {\n transform: translateX(-50%) scale(1.4);\n box-shadow:\n 0 0 0 3px var(--mj-timeline-marker-fill),\n 0 0 12px rgba(70, 120, 168, 0.5);\n}\n\n/* Pulse animation for selected marker */\n.mj-timeline__event--focused .mj-timeline__marker::after {\n content: '';\n position: absolute;\n top: -4px;\n left: -4px;\n right: -4px;\n bottom: -4px;\n border-radius: 50%;\n border: 2px solid var(--mj-timeline-accent);\n animation: mj-timeline-pulse 1.5s ease-out infinite;\n}\n\n@keyframes mj-timeline-pulse {\n 0% {\n transform: scale(1);\n opacity: 0.8;\n }\n 100% {\n transform: scale(1.8);\n opacity: 0;\n }\n}\n\n/* Selected date label - bolder */\n.mj-timeline__event--focused .mj-timeline__date-label {\n color: var(--mj-timeline-accent);\n font-weight: 600;\n}\n\n/* Card arrow gets accent color when selected */\n.mj-timeline__event--focused .mj-timeline__card::before {\n border-color: var(--mj-timeline-accent);\n background: var(--mj-timeline-accent-light);\n}\n\n/* ============================================================================\n CARD HEADER\n ============================================================================ */\n\n.mj-timeline__card-header {\n display: flex;\n align-items: flex-start;\n padding: var(--mj-timeline-card-padding);\n gap: 10px;\n}\n\n.mj-timeline__card-header-content {\n flex: 1;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n}\n\n.mj-timeline__card-icon {\n display: none;\n}\n\n.mj-timeline__card-titles {\n flex: 1;\n min-width: 0;\n}\n\n.mj-timeline__card-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-timeline-text-primary);\n line-height: 1.35;\n}\n\n.mj-timeline__card-subtitle {\n display: block;\n font-size: 13px;\n color: var(--mj-timeline-text-secondary);\n margin-top: 4px;\n}\n\n.mj-timeline__card-date {\n display: block;\n font-size: 12px;\n color: var(--mj-timeline-text-muted);\n margin-top: 6px;\n}\n\n.mj-timeline__card-date::before {\n display: none;\n}\n\n.mj-timeline__card-toggle {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: var(--mj-timeline-text-muted);\n border-radius: 4px;\n cursor: pointer;\n transition: all var(--mj-timeline-transition);\n font-size: 14px;\n}\n\n.mj-timeline__card-toggle:hover {\n background: var(--mj-timeline-accent-light);\n color: var(--mj-timeline-accent);\n}\n\n/* ============================================================================\n CARD IMAGE\n ============================================================================ */\n\n.mj-timeline__card-image {\n overflow: hidden;\n border-radius: 4px;\n flex-shrink: 0;\n}\n\n.mj-timeline__card-image img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.mj-timeline__card-image--left.mj-timeline__card-image--small {\n width: 48px;\n height: 48px;\n}\n\n.mj-timeline__card-image--left.mj-timeline__card-image--medium {\n width: 72px;\n height: 72px;\n}\n\n.mj-timeline__card-image--left.mj-timeline__card-image--large {\n width: 96px;\n height: 96px;\n}\n\n.mj-timeline__card-image--top {\n width: calc(100% - 32px);\n max-height: 180px;\n margin: 0 16px 12px;\n}\n\n/* ============================================================================\n CARD BODY\n ============================================================================ */\n\n.mj-timeline__card-body {\n padding: 0 var(--mj-timeline-card-padding) var(--mj-timeline-card-padding);\n}\n\n.mj-timeline__card-body--collapsed {\n display: none;\n}\n\n.mj-timeline__card-description {\n font-size: 13px;\n line-height: 1.6;\n color: var(--mj-timeline-text-secondary);\n margin-bottom: 12px;\n}\n\n.mj-timeline__card-description--clamped {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.mj-timeline__card-description:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n CARD FIELDS\n ============================================================================ */\n\n.mj-timeline__card-fields {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 12px;\n}\n\n.mj-timeline__card-fields--summary {\n padding-top: 12px;\n border-top: 1px solid var(--mj-timeline-line-color);\n margin-top: 12px;\n}\n\n.mj-timeline__card-fields--expanded {\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--mj-timeline-line-color);\n}\n\n.mj-timeline__card-field {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 12px;\n}\n\n.mj-timeline__card-field-icon {\n color: var(--mj-timeline-text-muted);\n font-size: 10px;\n}\n\n.mj-timeline__card-field-label {\n color: var(--mj-timeline-text-muted);\n}\n\n.mj-timeline__card-field-value {\n color: var(--mj-timeline-text-secondary);\n font-weight: 500;\n}\n\n/* ============================================================================\n CARD ACTIONS\n ============================================================================ */\n\n.mj-timeline__card-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 0 var(--mj-timeline-card-padding) var(--mj-timeline-card-padding);\n justify-content: flex-end;\n}\n\n.mj-timeline__card-actions--hover-only {\n opacity: 0;\n transition: opacity var(--mj-timeline-transition);\n}\n\n.mj-timeline__card:hover .mj-timeline__card-actions--hover-only,\n.mj-timeline__card:focus-within .mj-timeline__card-actions--hover-only {\n opacity: 1;\n}\n\n.mj-timeline__action {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 6px 12px;\n font-size: 12px;\n font-weight: 500;\n border: 1px solid;\n border-radius: 4px;\n cursor: pointer;\n transition: all var(--mj-timeline-transition);\n}\n\n.mj-timeline__action:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.mj-timeline__action i {\n font-size: 10px;\n}\n\n.mj-timeline__action--primary {\n background: var(--mj-timeline-accent);\n border-color: var(--mj-timeline-accent);\n color: #ffffff;\n}\n\n.mj-timeline__action--primary:hover:not(:disabled) {\n filter: brightness(1.1);\n}\n\n.mj-timeline__action--secondary {\n background: transparent;\n border-color: var(--mj-timeline-card-border);\n color: var(--mj-timeline-text-primary);\n}\n\n.mj-timeline__action--secondary:hover:not(:disabled) {\n background: var(--mj-timeline-accent-light);\n border-color: var(--mj-timeline-accent);\n color: var(--mj-timeline-accent);\n}\n\n.mj-timeline__action--danger {\n background: #cf222e;\n border-color: #cf222e;\n color: #ffffff;\n}\n\n.mj-timeline__action--link {\n background: transparent;\n border-color: transparent;\n color: var(--mj-timeline-accent);\n}\n\n.mj-timeline__action--link:hover:not(:disabled) {\n text-decoration: underline;\n}\n\n/* ============================================================================\n VIRTUAL SCROLL\n ============================================================================ */\n\n.mj-timeline-scroll-sentinel {\n height: 1px;\n width: 100%;\n visibility: hidden;\n}\n\n/* ============================================================================\n SINGLE LAYOUT - Cards only on right side\n ============================================================================ */\n\n.mj-timeline--single .mj-timeline__axis::before {\n left: 24px;\n transform: none;\n}\n\n.mj-timeline--single .mj-timeline__segment-header {\n margin-left: 12px;\n}\n\n.mj-timeline--single .mj-timeline__event {\n padding-right: 0;\n padding-left: 56px;\n justify-content: flex-start;\n}\n\n.mj-timeline--single .mj-timeline__event--odd {\n padding-left: 56px;\n flex-direction: row;\n}\n\n.mj-timeline--single .mj-timeline__marker {\n left: 24px;\n transform: translateX(-50%);\n}\n\n.mj-timeline--single .mj-timeline__date-label {\n display: none;\n}\n\n.mj-timeline--single .mj-timeline__card::before {\n left: -6px;\n right: auto;\n border-right: none;\n border-top: none;\n border-left: 1px solid var(--mj-timeline-card-border);\n border-bottom: 1px solid var(--mj-timeline-card-border);\n}\n\n.mj-timeline--single .mj-timeline__card {\n max-width: none;\n}\n\n/* ============================================================================\n ALTERNATING LAYOUT - Explicit class (same as default vertical)\n ============================================================================ */\n\n.mj-timeline--alternating .mj-timeline__segment-header {\n left: 50%;\n transform: translateX(-50%);\n}\n\n/* ============================================================================\n HORIZONTAL LAYOUT\n ============================================================================ */\n\n.mj-timeline--horizontal {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n align-items: flex-start;\n overflow-x: auto;\n overflow-y: hidden;\n padding: 80px 40px 24px;\n min-width: 100%;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment {\n flex: 0 0 auto;\n display: flex;\n flex-direction: column;\n margin-bottom: 0;\n margin-right: 40px;\n position: relative;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment:last-child {\n margin-right: 0;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment-header {\n position: absolute;\n top: -56px;\n left: 0;\n margin-bottom: 0;\n white-space: nowrap;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment-content {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n}\n\n.mj-timeline--horizontal .mj-timeline__axis {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n padding-left: 0;\n min-width: max-content;\n gap: 0;\n}\n\n/* Horizontal axis line */\n.mj-timeline--horizontal .mj-timeline__axis::before {\n left: 0;\n right: 0;\n top: 0;\n bottom: auto;\n width: 100%;\n height: var(--mj-timeline-line-width);\n transform: none;\n}\n\n.mj-timeline--horizontal .mj-timeline__event {\n flex-direction: column;\n align-items: center;\n margin-bottom: 0;\n margin-right: 48px;\n padding: 0;\n padding-top: 32px;\n min-width: 180px;\n max-width: 280px;\n width: auto;\n}\n\n.mj-timeline--horizontal .mj-timeline__event--odd {\n padding-left: 0;\n flex-direction: column;\n}\n\n.mj-timeline--horizontal .mj-timeline__event:last-child {\n margin-right: 0;\n}\n\n.mj-timeline--horizontal .mj-timeline__marker {\n position: absolute;\n left: 50%;\n /* Position so marker is centered on the axis line (top: 0 of axis) */\n /* Event has padding-top: 32px, marker is 14px, so top: -(32 + 7) = -39px for center */\n top: calc(-32px - var(--mj-timeline-marker-size) / 2);\n transform: translateX(-50%);\n margin: 0;\n}\n\n.mj-timeline--horizontal .mj-timeline__event:hover .mj-timeline__marker {\n transform: translateX(-50%) scale(1.2);\n}\n\n.mj-timeline--horizontal .mj-timeline__date-label {\n position: absolute;\n top: -40px;\n left: 50%;\n right: auto;\n transform: translateX(-50%);\n text-align: center;\n font-size: 12px;\n}\n\n.mj-timeline--horizontal .mj-timeline__card {\n max-width: none;\n width: 100%;\n min-width: 0;\n}\n\n.mj-timeline--horizontal .mj-timeline__card::before {\n display: none;\n}\n\n/* Horizontal focused marker override */\n.mj-timeline--horizontal .mj-timeline__event--focused .mj-timeline__marker {\n transform: translateX(-50%) scale(1.4);\n}\n\n/* Horizontal collapsed segment - rotate 90\u00B0 CCW and compress */\n.mj-timeline--horizontal .mj-timeline__segment--collapsed {\n flex: 0 0 auto;\n width: 32px;\n min-width: 32px;\n margin-right: 4px;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment--collapsed .mj-timeline__segment-header {\n writing-mode: vertical-rl;\n text-orientation: mixed;\n transform: rotate(180deg);\n white-space: nowrap;\n padding: 8px 4px;\n margin: 0;\n top: 0;\n position: relative;\n}\n\n.mj-timeline--horizontal .mj-timeline__segment--collapsed .mj-timeline__segment-content {\n display: none;\n}\n\n/* ============================================================================\n RESPONSIVE\n ============================================================================ */\n\n@media (max-width: 900px) {\n /* Force single layout on smaller screens */\n .mj-timeline--alternating .mj-timeline__axis::before {\n left: 24px;\n transform: none;\n }\n\n .mj-timeline--alternating .mj-timeline__segment-header {\n left: auto;\n transform: none;\n margin-left: 12px;\n }\n\n .mj-timeline--alternating .mj-timeline__event {\n padding-right: 0;\n padding-left: 56px;\n justify-content: flex-start;\n }\n\n .mj-timeline--alternating .mj-timeline__event--odd {\n padding-left: 56px;\n padding-right: 0;\n flex-direction: row;\n }\n\n .mj-timeline--alternating .mj-timeline__marker {\n left: 24px;\n transform: translateX(-50%);\n }\n\n .mj-timeline--alternating .mj-timeline__event:hover .mj-timeline__marker {\n transform: translateX(-50%) scale(1.2);\n }\n\n .mj-timeline--alternating .mj-timeline__date-label {\n display: none;\n }\n\n .mj-timeline--alternating .mj-timeline__card::before {\n left: -6px;\n right: auto;\n border-right: none;\n border-top: none;\n border-left: 1px solid var(--mj-timeline-card-border);\n border-bottom: 1px solid var(--mj-timeline-card-border);\n }\n\n .mj-timeline--alternating .mj-timeline__card {\n max-width: none;\n }\n}\n\n@media (max-width: 600px) {\n mj-timeline {\n --mj-timeline-card-padding: 12px;\n --mj-timeline-gap: 16px;\n --mj-timeline-marker-size: 12px;\n }\n\n .mj-timeline {\n padding: 16px 12px;\n }\n\n .mj-timeline__card-title {\n font-size: 14px;\n }\n\n .mj-timeline--horizontal .mj-timeline__event {\n min-width: 160px;\n max-width: 220px;\n margin-right: 32px;\n }\n}\n\n/* Touch devices */\n@media (hover: none) and (pointer: coarse) {\n .mj-timeline__card-actions--hover-only {\n opacity: 1;\n }\n\n .mj-timeline__action {\n min-height: 40px;\n }\n}\n\n/* Reduced motion */\n@media (prefers-reduced-motion: reduce) {\n .mj-timeline__card,\n .mj-timeline__marker,\n .mj-timeline__segment-toggle {\n transition: none;\n }\n}\n"] }]
|
|
2007
|
+
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }], { groups: [{
|
|
2008
|
+
type: Input
|
|
2009
|
+
}], allowLoad: [{
|
|
2010
|
+
type: Input
|
|
2011
|
+
}], orientation: [{
|
|
2012
|
+
type: Input
|
|
2013
|
+
}], layout: [{
|
|
2014
|
+
type: Input
|
|
2015
|
+
}], sortOrder: [{
|
|
2016
|
+
type: Input
|
|
2017
|
+
}], segmentGrouping: [{
|
|
2018
|
+
type: Input
|
|
2019
|
+
}], defaultCardConfig: [{
|
|
2020
|
+
type: Input
|
|
2021
|
+
}], virtualScroll: [{
|
|
2022
|
+
type: Input
|
|
2023
|
+
}], segmentsCollapsible: [{
|
|
2024
|
+
type: Input
|
|
2025
|
+
}], segmentsDefaultExpanded: [{
|
|
2026
|
+
type: Input
|
|
2027
|
+
}], emptyMessage: [{
|
|
2028
|
+
type: Input
|
|
2029
|
+
}], emptyIcon: [{
|
|
2030
|
+
type: Input
|
|
2031
|
+
}], loadingMessage: [{
|
|
2032
|
+
type: Input
|
|
2033
|
+
}], ariaLabel: [{
|
|
193
2034
|
type: Input
|
|
194
|
-
}],
|
|
2035
|
+
}], enableKeyboardNavigation: [{
|
|
195
2036
|
type: Input
|
|
196
|
-
}],
|
|
2037
|
+
}], selectedEventId: [{
|
|
197
2038
|
type: Input
|
|
2039
|
+
}], beforeEventClick: [{
|
|
2040
|
+
type: Output
|
|
2041
|
+
}], beforeEventExpand: [{
|
|
2042
|
+
type: Output
|
|
2043
|
+
}], beforeEventCollapse: [{
|
|
2044
|
+
type: Output
|
|
2045
|
+
}], beforeEventHover: [{
|
|
2046
|
+
type: Output
|
|
2047
|
+
}], beforeActionClick: [{
|
|
2048
|
+
type: Output
|
|
2049
|
+
}], beforeSegmentExpand: [{
|
|
2050
|
+
type: Output
|
|
2051
|
+
}], beforeSegmentCollapse: [{
|
|
2052
|
+
type: Output
|
|
2053
|
+
}], beforeLoad: [{
|
|
2054
|
+
type: Output
|
|
2055
|
+
}], afterEventClick: [{
|
|
2056
|
+
type: Output
|
|
2057
|
+
}], afterEventExpand: [{
|
|
2058
|
+
type: Output
|
|
2059
|
+
}], afterEventCollapse: [{
|
|
2060
|
+
type: Output
|
|
2061
|
+
}], afterEventHover: [{
|
|
2062
|
+
type: Output
|
|
2063
|
+
}], afterActionClick: [{
|
|
2064
|
+
type: Output
|
|
2065
|
+
}], afterSegmentExpand: [{
|
|
2066
|
+
type: Output
|
|
2067
|
+
}], afterSegmentCollapse: [{
|
|
2068
|
+
type: Output
|
|
2069
|
+
}], afterLoad: [{
|
|
2070
|
+
type: Output
|
|
2071
|
+
}], cardTemplate: [{
|
|
2072
|
+
type: ContentChild,
|
|
2073
|
+
args: ['cardTemplate']
|
|
2074
|
+
}], headerTemplate: [{
|
|
2075
|
+
type: ContentChild,
|
|
2076
|
+
args: ['headerTemplate']
|
|
2077
|
+
}], bodyTemplate: [{
|
|
2078
|
+
type: ContentChild,
|
|
2079
|
+
args: ['bodyTemplate']
|
|
2080
|
+
}], actionsTemplate: [{
|
|
2081
|
+
type: ContentChild,
|
|
2082
|
+
args: ['actionsTemplate']
|
|
2083
|
+
}], segmentHeaderTemplate: [{
|
|
2084
|
+
type: ContentChild,
|
|
2085
|
+
args: ['segmentHeaderTemplate']
|
|
2086
|
+
}], emptyTemplate: [{
|
|
2087
|
+
type: ContentChild,
|
|
2088
|
+
args: ['emptyTemplate']
|
|
2089
|
+
}], loadingTemplate: [{
|
|
2090
|
+
type: ContentChild,
|
|
2091
|
+
args: ['loadingTemplate']
|
|
2092
|
+
}], scrollContainer: [{
|
|
2093
|
+
type: ViewChild,
|
|
2094
|
+
args: ['scrollContainer']
|
|
198
2095
|
}] }); })();
|
|
199
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TimelineComponent, { className: "TimelineComponent", filePath: "src/lib/component/timeline.component.ts", lineNumber:
|
|
2096
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TimelineComponent, { className: "TimelineComponent", filePath: "src/lib/component/timeline.component.ts", lineNumber: 143 }); })();
|
|
200
2097
|
//# sourceMappingURL=timeline.component.js.map
|