ar-timeline-view 0.0.0-stage → 1.0.2

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.
@@ -0,0 +1,1341 @@
1
+ // src/api.ts
2
+ var e = class extends Error {
3
+ status;
4
+ body;
5
+ constructor(e, t) {
6
+ super(`REST request failed with status ${e}`), this.status = e, this.body = t, this.name = "ApiError";
7
+ }
8
+ }, t = (e) => encodeURIComponent(String(e)), n = class {
9
+ fetchImpl;
10
+ baseUrl;
11
+ constructor(e, t = (...e) => globalThis.fetch(...e)) {
12
+ this.fetchImpl = t, this.baseUrl = e.replace(/\/+$/, "");
13
+ }
14
+ getView(e) {
15
+ return this.request(`/views/${t(e)}`, { method: "GET" });
16
+ }
17
+ moveEntry(e, n, r, i, a) {
18
+ return this.request(`/views/${t(e)}/timelines/${t(n)}/entries/${t(r)}`, {
19
+ method: "PATCH",
20
+ headers: { "Content-Type": "application/json" },
21
+ body: JSON.stringify(a === void 0 ? i : {
22
+ ...i,
23
+ version: a
24
+ })
25
+ });
26
+ }
27
+ async request(t, n) {
28
+ let r = await this.fetchImpl(this.baseUrl + t, {
29
+ ...n,
30
+ headers: {
31
+ Accept: "application/json",
32
+ ...n.headers
33
+ }
34
+ }), i = await r.text(), a;
35
+ try {
36
+ a = i ? JSON.parse(i) : void 0;
37
+ } catch {
38
+ a = i;
39
+ }
40
+ if (!r.ok) throw new e(r.status, a);
41
+ return a;
42
+ }
43
+ }, r = 4;
44
+ function i(e, t, n, r) {
45
+ let i = Math.round(e.left - (t.left + n * t.width)), a = r === "above" ? Math.min(0, Math.round(e.bottom - t.bottom)) : Math.max(1, Math.round(e.top - t.top));
46
+ return {
47
+ x: i,
48
+ y: a === 0 ? 0 : a
49
+ };
50
+ }
51
+ var a = (e, t) => Math.max(0, e.top - t, t - e.bottom);
52
+ function o(e, t, n) {
53
+ let r = document.elementFromPoint(t, n)?.closest(".tlv-line, .tlv-area");
54
+ if (r && e.contains(r)) return r;
55
+ let i = null, o = Infinity;
56
+ for (let t of e.querySelectorAll(".tlv-area")) {
57
+ let e = a(t.getBoundingClientRect(), n);
58
+ e < o && ([i, o] = [t, e]);
59
+ }
60
+ return i;
61
+ }
62
+ function s(e, t) {
63
+ if (!e) return;
64
+ let n = e.dataset.placement;
65
+ if (n === "above" || n === "below") return n;
66
+ let r = e.closest(".tlv-bar")?.getBoundingClientRect();
67
+ return r && t > (r.top + r.bottom) / 2 ? "below" : "above";
68
+ }
69
+ function c(e, t, n, r) {
70
+ let i = [...e.querySelectorAll(":scope > .tlv-marker-line")].filter((e) => e.dataset.marker === t), a = [...e.querySelectorAll(".tlv-timeline")], o = (e) => a.findIndex((t) => t.dataset.timeline === e), s = r ? a.indexOf(r) : -1, c = i.find((e) => o(e.dataset.from) <= s && s <= o(e.dataset.to)) ?? (n === "above" ? i[0] : i[i.length - 1]), l = n === "above" ? c?.dataset.from : c?.dataset.to, u = a.find((e) => e.dataset.timeline === l)?.querySelector(`.tlv-area--${n}`);
71
+ if (!c || !u) return;
72
+ let d = u.getBoundingClientRect(), f = c.getBoundingClientRect();
73
+ return {
74
+ area: u,
75
+ side: n,
76
+ at: (f.left + f.width / 2 - d.left) / d.width
77
+ };
78
+ }
79
+ function l(e) {
80
+ let t = e.dataset.timeline, n = e.dataset.placement;
81
+ return n === "on" ? {
82
+ timeline: t,
83
+ placement: n,
84
+ line: Number(e.dataset.line),
85
+ newLine: e.dataset.newLine === "true"
86
+ } : {
87
+ timeline: t,
88
+ placement: n
89
+ };
90
+ }
91
+ function u(e, t, n, a, u) {
92
+ let d, f = (e, t) => {
93
+ let n = !1, i = (t) => {
94
+ Math.hypot(t.clientX - e.clientX, t.clientY - e.clientY) >= r && (n = !0);
95
+ }, o = (e) => {
96
+ s(), n || a?.(t, e);
97
+ }, s = () => {
98
+ window.removeEventListener("pointermove", i), window.removeEventListener("pointerup", o), window.removeEventListener("pointercancel", s), d = void 0;
99
+ };
100
+ window.addEventListener("pointermove", i), window.addEventListener("pointerup", o), window.addEventListener("pointercancel", s), d = s;
101
+ }, p = (a) => {
102
+ if (a.button !== 0) return;
103
+ let p = a.target, m = p.closest("[data-entry]");
104
+ if (!m || !e.contains(m)) {
105
+ let t = p.closest(".tlv-timeline, .tlv-title");
106
+ t && e.contains(t) && f(a, p);
107
+ return;
108
+ }
109
+ a.preventDefault();
110
+ let h = m.getBoundingClientRect(), g = {
111
+ x: a.clientX - h.left,
112
+ y: a.clientY - h.top
113
+ }, _ = m.classList.contains("tlv-callout"), v = m.dataset.marker, y = (t, n) => {
114
+ let r = o(e, t, n), i = s(r, n);
115
+ return i ? c(e, v, i, r?.closest(".tlv-timeline") ?? null) : void 0;
116
+ }, b, x = null, S = (e) => {
117
+ e !== x && (x?.classList.remove("tlv-drop"), e?.classList.add("tlv-drop"), x = e);
118
+ }, C = (t) => {
119
+ if (!b) {
120
+ if (Math.hypot(t.clientX - a.clientX, t.clientY - a.clientY) < r) return;
121
+ b = m.cloneNode(!0), b.classList.add("tlv-ghost"), b.removeAttribute("data-entry"), Object.assign(b.style, {
122
+ width: `${h.width}px`,
123
+ height: `${h.height}px`,
124
+ margin: "0"
125
+ }), e.append(b), m.classList.add("tlv-dragging"), e.classList.add("tlv--dragging");
126
+ }
127
+ b.style.left = `${t.clientX - g.x}px`, b.style.top = `${t.clientY - g.y}px`, S(v === void 0 ? o(e, t.clientX, t.clientY) : y(t.clientX, t.clientY)?.area ?? null);
128
+ }, w = (r) => {
129
+ let a = r && b ? o(e, r.clientX, r.clientY) : null, s = b?.getBoundingClientRect(), c = b !== void 0;
130
+ if (d?.(), r && !c) {
131
+ n?.(m, r);
132
+ return;
133
+ }
134
+ if (!a || !s || !r) return;
135
+ if (v !== void 0) {
136
+ let e = y(r.clientX, r.clientY);
137
+ if (e) {
138
+ let t = i(s, e.area.getBoundingClientRect(), e.at, e.side);
139
+ u?.({
140
+ marker: v,
141
+ offset: t
142
+ });
143
+ }
144
+ return;
145
+ }
146
+ let f = l(a), p = {
147
+ timeline: m.dataset.timeline,
148
+ task: m.dataset.task,
149
+ target: f
150
+ };
151
+ if (f.placement !== "on") {
152
+ let e = {
153
+ left: r.clientX,
154
+ right: r.clientX,
155
+ top: r.clientY,
156
+ bottom: r.clientY,
157
+ width: 0
158
+ };
159
+ p.offset = i(_ ? s : e, a.getBoundingClientRect(), Number(m.dataset.at), f.placement);
160
+ }
161
+ t(p);
162
+ }, T = (e) => w(e), E = () => w(), D = (e) => {
163
+ e.key === "Escape" && w();
164
+ };
165
+ window.addEventListener("pointermove", C), window.addEventListener("pointerup", T), window.addEventListener("pointercancel", E), window.addEventListener("keydown", D), d = () => {
166
+ window.removeEventListener("pointermove", C), window.removeEventListener("pointerup", T), window.removeEventListener("pointercancel", E), window.removeEventListener("keydown", D), S(null), b?.remove(), m.classList.remove("tlv-dragging"), e.classList.remove("tlv--dragging"), d = void 0;
167
+ };
168
+ };
169
+ return e.addEventListener("pointerdown", p), () => {
170
+ d?.(), e.removeEventListener("pointerdown", p);
171
+ };
172
+ }
173
+ // src/dates.ts
174
+ var d = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/;
175
+ function f(e) {
176
+ let t = d.exec(e);
177
+ if (!t) throw Error(`Invalid date "${e}": expected YYYY-MM-DD or YYYY-MM-DDTHH:mm`);
178
+ let [, n, r, i, a = "0", o = "0"] = t, s = new Date(Date.UTC(+n, r - 1, +i, +a, +o));
179
+ if (s.getUTCMonth() !== r - 1 || s.getUTCDate() !== +i || +a > 23 || +o > 59) throw Error(`Invalid date "${e}"`);
180
+ return s;
181
+ }
182
+ var p = (e) => String(e).padStart(2, "0");
183
+ function m(e) {
184
+ let t = `${e.getUTCFullYear()}-${p(e.getUTCMonth() + 1)}-${p(e.getUTCDate())}`, n = e.getUTCHours(), r = e.getUTCMinutes();
185
+ return n || r ? `${t}T${p(n)}:${p(r)}` : t;
186
+ }
187
+ function h(e, t) {
188
+ return new Date(Date.UTC(e, t + 1, 0)).getUTCDate();
189
+ }
190
+ function g(e, t) {
191
+ let n = e.getUTCFullYear() * 12 + e.getUTCMonth() + t, r = Math.floor(n / 12), i = n - r * 12, a = Math.min(e.getUTCDate(), h(r, i));
192
+ return new Date(Date.UTC(r, i, a, e.getUTCHours(), e.getUTCMinutes(), e.getUTCSeconds()));
193
+ }
194
+ function _(e, t) {
195
+ let n = new Date(e.getTime());
196
+ return n.setUTCDate(n.getUTCDate() + t), n;
197
+ }
198
+ // src/duration.ts
199
+ var v = /^(\d+)([hdwmy])$/;
200
+ function y(e) {
201
+ let t = v.exec(e.trim());
202
+ if (!t) throw Error(`Invalid duration "${e}": expected a whole number plus h, d, w, m or y`);
203
+ return {
204
+ amount: Number(t[1]),
205
+ unit: t[2]
206
+ };
207
+ }
208
+ function b(e, { amount: t, unit: n }) {
209
+ switch (n) {
210
+ case "h": return new Date(e.getTime() + t * 36e5);
211
+ case "d": return _(e, t);
212
+ case "w": return _(e, t * 7);
213
+ case "m": return g(e, t);
214
+ case "y": return g(e, t * 12);
215
+ }
216
+ }
217
+ function x(e) {
218
+ return e === void 0 || e.trim() === "" || y(e).amount === 0;
219
+ }
220
+ // src/model.ts
221
+ var S = class extends Error {
222
+ code;
223
+ constructor(e, t) {
224
+ super(t), this.code = e, this.name = "MoveError";
225
+ }
226
+ }, C = [
227
+ "above",
228
+ "on",
229
+ "below"
230
+ ], w = [
231
+ "hour",
232
+ "day",
233
+ "week",
234
+ "month",
235
+ "year"
236
+ ], T = (e, t) => String(e) === String(t), E = (e) => Number.isInteger(e) && e >= 0, D = (e) => typeof e == "object" && !!e && Number.isFinite(e.x) && Number.isFinite(e.y), ee = (e) => ({
237
+ x: 0,
238
+ y: e === "above" ? -16 : 16
239
+ });
240
+ function te(e, t) {
241
+ if (!D(e)) return "callout_offset needs numeric x and y";
242
+ if (t === "above" && e.y > 0) return "callout_offset.y must be ≤ 0 above the bar";
243
+ if (t === "below" && e.y <= 0) return "callout_offset.y must be > 0 below the bar";
244
+ }
245
+ var O = class e {
246
+ view;
247
+ tasks;
248
+ timelines;
249
+ markers;
250
+ version;
251
+ constructor(e, t, n, r = []) {
252
+ this.view = e, this.tasks = t, this.timelines = n, this.markers = r;
253
+ }
254
+ static fromData(t) {
255
+ if (!t?.view) throw Error("Timeline data needs a `view`");
256
+ let n = t.view, r = n.scale ?? "day";
257
+ if (!w.includes(r)) throw Error(`Unknown scale "${r}"`);
258
+ if (n.show_dates !== void 0 && typeof n.show_dates != "boolean") throw Error("view.show_dates must be true or false");
259
+ if (n.absolute !== void 0 && typeof n.absolute != "boolean") throw Error("view.absolute must be true or false");
260
+ if (n.selection !== void 0 && typeof n.selection != "boolean") throw Error("view.selection must be true or false");
261
+ let i = {
262
+ id: n.id,
263
+ name: n.name ?? "",
264
+ absolute: n.absolute ?? !1,
265
+ scale: r,
266
+ namePosition: n.name_position ?? "left",
267
+ milestoneSymbol: n.milestone_symbol ?? "diamond",
268
+ showDates: n.show_dates ?? !0,
269
+ selection: n.selection ?? !0
270
+ }, a = (t.tasks ?? []).map(se), o = /* @__PURE__ */ new Set();
271
+ for (let e of a) {
272
+ if (o.has(String(e.id))) throw Error(`Duplicate task id ${e.id}`);
273
+ o.add(String(e.id));
274
+ }
275
+ let s = (t.markers ?? []).map(ae), c = /* @__PURE__ */ new Set();
276
+ for (let e of s) {
277
+ if (c.has(String(e.id))) throw Error(`Duplicate marker id ${e.id}`);
278
+ c.add(String(e.id));
279
+ }
280
+ let l = new e(i, a, [], s);
281
+ return l.timelines = (t.timelines ?? []).map((e) => l.timelineFromData(e)), l.version = t.version, l;
282
+ }
283
+ toData() {
284
+ let e = this.view;
285
+ return {
286
+ ...this.version !== void 0 && { version: this.version },
287
+ view: {
288
+ ...e.id !== void 0 && { id: e.id },
289
+ name: e.name,
290
+ absolute: e.absolute,
291
+ scale: e.scale,
292
+ name_position: e.namePosition,
293
+ milestone_symbol: e.milestoneSymbol,
294
+ show_dates: e.showDates,
295
+ selection: e.selection
296
+ },
297
+ tasks: this.tasks.map(ce),
298
+ timelines: this.timelines.map(le),
299
+ ...this.markers.length > 0 && { markers: this.markers.map(oe) }
300
+ };
301
+ }
302
+ timelineFromData(e) {
303
+ let t = f(e.start_date), n = f(e.finish_date);
304
+ if (n <= t) throw Error(`Timeline ${e.id}: finish_date must be after start_date`);
305
+ let r = [], i = [];
306
+ for (let t of e.entries ?? []) if (typeof t == "object" && t && "marker" in t) {
307
+ let n = this.findMarker(t.marker);
308
+ if (!n) throw Error(`Timeline ${e.id}: unknown marker ${t.marker}`);
309
+ if (Object.keys(t).some((e) => e !== "marker")) throw Error(`Timeline ${e.id}: a marker entry has only \`marker\``);
310
+ if (i.some((e) => T(e, n.id))) throw Error(`Timeline ${e.id}: marker ${n.id} appears more than once`);
311
+ i.push(n.id);
312
+ } else r.push(this.entryFromData(e.id, t));
313
+ let a = /* @__PURE__ */ new Set();
314
+ for (let t of r) {
315
+ if (a.has(String(t.taskId))) throw Error(`Timeline ${e.id}: task ${t.taskId} appears more than once`);
316
+ a.add(String(t.taskId));
317
+ }
318
+ return {
319
+ id: e.id,
320
+ name: e.name ?? "",
321
+ start: t,
322
+ finish: n,
323
+ entries: r,
324
+ markers: i
325
+ };
326
+ }
327
+ entryFromData(e, t) {
328
+ let n = `Timeline ${e}, task ${t.task}`;
329
+ if (!this.findTask(t.task)) throw Error(`${n}: unknown task`);
330
+ if (t.callout_offset === void 0) {
331
+ let e = t.line ?? 0;
332
+ if (!E(e)) throw Error(`${n}: line must be a whole number ≥ 0`);
333
+ return {
334
+ taskId: t.task,
335
+ placement: "on",
336
+ line: e
337
+ };
338
+ }
339
+ if (t.line !== void 0) throw Error(`${n}: a callout (with callout_offset) has no line`);
340
+ let r = t.callout_offset;
341
+ if (!D(r)) throw Error(`${n}: callout_offset needs numeric x and y`);
342
+ let i = r.y > 0 ? "below" : "above";
343
+ return {
344
+ taskId: t.task,
345
+ placement: i,
346
+ calloutOffset: {
347
+ x: r.x,
348
+ y: r.y === 0 ? 0 : r.y
349
+ }
350
+ };
351
+ }
352
+ findTask(e) {
353
+ return this.tasks.find((t) => T(t.id, e));
354
+ }
355
+ setMarkerOffset(e, t) {
356
+ let n = this.findMarker(e);
357
+ if (!n) throw new S("not_found", `Marker ${e} not found`);
358
+ if (!D(t)) throw new S("invalid", "marker_offset needs numeric x and y");
359
+ return n.offset = {
360
+ x: t.x,
361
+ y: t.y === 0 ? 0 : t.y
362
+ }, n;
363
+ }
364
+ findMarker(e) {
365
+ return this.markers.find((t) => T(t.id, e));
366
+ }
367
+ findTimeline(e) {
368
+ return this.timelines.find((t) => T(t.id, e));
369
+ }
370
+ findEntry(e, t) {
371
+ return this.findTimeline(e)?.entries.find((e) => T(e.taskId, t));
372
+ }
373
+ moveEntry(e, t, n) {
374
+ let r = this.findTimeline(e);
375
+ if (!r) throw new S("not_found", `Timeline ${e} not found`);
376
+ let i = r.entries.findIndex((e) => T(e.taskId, t));
377
+ if (i < 0) throw new S("not_found", `Task ${t} is not on timeline ${e}`);
378
+ let a = r.entries[i], o = n.timelineId === void 0 ? r : this.findTimeline(n.timelineId);
379
+ if (!o) throw new S("not_found", `Timeline ${n.timelineId} not found`);
380
+ if (o !== r && o.entries.some((e) => T(e.taskId, t))) throw new S("conflict", `Task ${t} is already on timeline ${o.id}`);
381
+ let s = n.placement ?? a.placement;
382
+ if (!C.includes(s)) throw new S("invalid", `Unknown placement "${s}"`);
383
+ let c;
384
+ if (s === "on") {
385
+ if (n.calloutOffset !== void 0) throw new S("invalid", "callout_offset is only valid above or below the bar");
386
+ let e = n.line ?? (o === r && a.placement === "on" ? a.line : 0);
387
+ if (!E(e)) throw new S("invalid", "line must be a whole number ≥ 0");
388
+ let t = M(o);
389
+ if (e > t) throw new S("invalid", `line ${e} is past the bar's last line + 1 (${t})`);
390
+ if (n.newLine !== void 0 && typeof n.newLine != "boolean") throw new S("invalid", "new_line must be true or false");
391
+ if (n.newLine) for (let t of o.entries) t.placement === "on" && t.line >= e && t.line++;
392
+ c = {
393
+ taskId: a.taskId,
394
+ placement: s,
395
+ line: e
396
+ };
397
+ } else {
398
+ if (n.line !== void 0 || n.newLine !== void 0) throw new S("invalid", "callouts have no line; use callout_offset");
399
+ let e = n.calloutOffset ?? ne(a, s), t = te(e, s);
400
+ if (t) throw new S("invalid", t);
401
+ c = {
402
+ taskId: a.taskId,
403
+ placement: s,
404
+ calloutOffset: {
405
+ x: e.x,
406
+ y: e.y
407
+ }
408
+ };
409
+ }
410
+ return o === r ? r.entries[i] = c : (r.entries.splice(i, 1), o.entries.push(c)), j(r), o !== r && j(o), o === r ? [r] : [r, o];
411
+ }
412
+ moveTimeline(e, t) {
413
+ let n = this.timelines.findIndex((t) => T(t.id, e));
414
+ if (n < 0) throw Error(`Timeline ${e} not found`);
415
+ let r = n + t;
416
+ if (!Number.isInteger(t) || r < 0 || r >= this.timelines.length || r === n) return !1;
417
+ let [i] = this.timelines.splice(n, 1);
418
+ return this.timelines.splice(r, 0, i), !0;
419
+ }
420
+ replaceTimelines(e) {
421
+ for (let t of e) {
422
+ let e = this.timelineFromData(t), n = this.timelines.findIndex((e) => T(e.id, t.id));
423
+ n >= 0 ? this.timelines[n] = e : this.timelines.push(e);
424
+ }
425
+ }
426
+ };
427
+ function ne(e, t) {
428
+ if (e.placement === "on") return ee(t);
429
+ let { x: n, y: r } = e.calloutOffset;
430
+ return e.placement === t ? {
431
+ x: n,
432
+ y: r
433
+ } : {
434
+ x: n,
435
+ y: t === "below" ? Math.max(1, -r) : -r
436
+ };
437
+ }
438
+ function k(e) {
439
+ if (e.timelines.length === 0) return;
440
+ let t = e.timelines[0].start, n = e.timelines[0].finish;
441
+ for (let r of e.timelines) r.start < t && (t = r.start), r.finish > n && (n = r.finish);
442
+ return {
443
+ start: t,
444
+ finish: n
445
+ };
446
+ }
447
+ function A(e) {
448
+ let t = e.entries.flatMap((e) => e.placement === "on" ? [e.line] : []);
449
+ return t.length ? Math.min(...t) : 0;
450
+ }
451
+ function j(e) {
452
+ let t = A(e);
453
+ if (t !== 0) for (let n of e.entries) n.placement === "on" && (n.line -= t);
454
+ }
455
+ function M(e) {
456
+ let t = 1;
457
+ for (let n of e.entries) n.placement === "on" && (t = Math.max(t, n.line + 1));
458
+ return t;
459
+ }
460
+ function re(e) {
461
+ return typeof CSS < "u" && typeof CSS.supports == "function" ? CSS.supports("color", e) : e.trim() !== "" && !/[;{}]/.test(e);
462
+ }
463
+ var ie = {
464
+ x: 0,
465
+ y: -16
466
+ };
467
+ function ae(e) {
468
+ if (e?.id === void 0 || e.id === null) throw Error("Every marker needs an id");
469
+ let t = `Marker ${e.id}`;
470
+ if (e.global !== void 0 && typeof e.global != "boolean") throw Error(`${t}: global must be true or false`);
471
+ if (e.color !== void 0 && (typeof e.color != "string" || !re(e.color))) throw Error(`${t}: color must be a CSS color`);
472
+ let n = e.marker_offset ?? ie;
473
+ if (!D(n)) throw Error(`${t}: marker_offset needs numeric x and y`);
474
+ return {
475
+ id: e.id,
476
+ text: e.text ?? "",
477
+ date: f(e.date),
478
+ global: e.global ?? !1,
479
+ color: e.color,
480
+ offset: {
481
+ x: n.x,
482
+ y: n.y === 0 ? 0 : n.y
483
+ }
484
+ };
485
+ }
486
+ function oe(e) {
487
+ return {
488
+ id: e.id,
489
+ text: e.text,
490
+ date: m(e.date),
491
+ ...e.global && { global: !0 },
492
+ ...e.color !== void 0 && { color: e.color },
493
+ marker_offset: { ...e.offset }
494
+ };
495
+ }
496
+ function se(e) {
497
+ if (e.id === void 0 || e.id === null) throw Error("Every task needs an id");
498
+ let t = f(e.start_date), n = x(e.duration);
499
+ if (e.color !== void 0 && (typeof e.color != "string" || !re(e.color))) throw Error(`Task ${e.id}: color must be a CSS color`);
500
+ return {
501
+ id: e.id,
502
+ text: e.text ?? "",
503
+ start: t,
504
+ finish: n ? t : b(t, y(e.duration)),
505
+ duration: e.duration,
506
+ isMilestone: n,
507
+ symbol: e.symbol,
508
+ color: e.color
509
+ };
510
+ }
511
+ function ce(e) {
512
+ return {
513
+ id: e.id,
514
+ text: e.text,
515
+ start_date: m(e.start),
516
+ ...e.duration !== void 0 && { duration: e.duration },
517
+ ...e.symbol !== void 0 && { symbol: e.symbol },
518
+ ...e.color !== void 0 && { color: e.color }
519
+ };
520
+ }
521
+ function le(e) {
522
+ return {
523
+ id: e.id,
524
+ name: e.name,
525
+ start_date: m(e.start),
526
+ finish_date: m(e.finish),
527
+ entries: [...e.entries.map((e) => e.placement === "on" ? {
528
+ task: e.taskId,
529
+ line: e.line
530
+ } : {
531
+ task: e.taskId,
532
+ callout_offset: { ...e.calloutOffset }
533
+ }), ...e.markers.map((e) => ({ marker: e }))]
534
+ };
535
+ }
536
+ function ue(e) {
537
+ return {
538
+ ...e.timeline_id !== void 0 && { timelineId: e.timeline_id },
539
+ ...e.placement !== void 0 && { placement: e.placement },
540
+ ...e.line !== void 0 && { line: e.line },
541
+ ...e.new_line !== void 0 && { newLine: e.new_line },
542
+ ...e.callout_offset !== void 0 && { calloutOffset: e.callout_offset }
543
+ };
544
+ }
545
+ // src/scale.ts
546
+ var N = [
547
+ "Jan",
548
+ "Feb",
549
+ "Mar",
550
+ "Apr",
551
+ "May",
552
+ "Jun",
553
+ "Jul",
554
+ "Aug",
555
+ "Sep",
556
+ "Oct",
557
+ "Nov",
558
+ "Dec"
559
+ ];
560
+ function P(e, t, n) {
561
+ return (e.getTime() - t.getTime()) / (n.getTime() - t.getTime());
562
+ }
563
+ function de(e, t) {
564
+ let n = new Date(e.getTime());
565
+ switch (t) {
566
+ case "hour": return n.setUTCMinutes(0, 0, 0), n < e && n.setUTCHours(n.getUTCHours() + 1), n;
567
+ case "day": return n.setUTCHours(0, 0, 0, 0), n < e ? _(n, 1) : n;
568
+ case "week": {
569
+ n.setUTCHours(0, 0, 0, 0);
570
+ let t = _(n, -((n.getUTCDay() + 6) % 7));
571
+ return t < e ? _(t, 7) : t;
572
+ }
573
+ case "month": {
574
+ let t = new Date(Date.UTC(n.getUTCFullYear(), n.getUTCMonth(), 1));
575
+ return t < e ? g(t, 1) : t;
576
+ }
577
+ case "year": {
578
+ let t = new Date(Date.UTC(n.getUTCFullYear(), 0, 1));
579
+ return t < e ? new Date(Date.UTC(n.getUTCFullYear() + 1, 0, 1)) : t;
580
+ }
581
+ }
582
+ }
583
+ function fe(e, t) {
584
+ switch (t) {
585
+ case "hour": return new Date(e.getTime() + 36e5);
586
+ case "day": return _(e, 1);
587
+ case "week": return _(e, 7);
588
+ case "month": return g(e, 1);
589
+ case "year": return g(e, 12);
590
+ }
591
+ }
592
+ function pe(e) {
593
+ let t = new Date(Date.UTC(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate()));
594
+ t.setUTCDate(t.getUTCDate() + 3 - (t.getUTCDay() + 6) % 7);
595
+ let n = Date.UTC(t.getUTCFullYear(), 0, 1);
596
+ return Math.ceil(((t.getTime() - n) / 864e5 + 1) / 7);
597
+ }
598
+ function F(e, t) {
599
+ let n = e.getTime();
600
+ switch (t) {
601
+ case "hour": return Math.floor(n / 36e5);
602
+ case "day": return Math.floor(n / 864e5);
603
+ case "week": return Math.floor((Math.floor(n / 864e5) + 3) / 7);
604
+ case "month": return e.getUTCFullYear() * 12 + e.getUTCMonth();
605
+ case "year": return e.getUTCFullYear();
606
+ }
607
+ }
608
+ function me(e, t, n) {
609
+ switch (t) {
610
+ case "hour": {
611
+ let t = `${String(e.getUTCHours()).padStart(2, "0")}:00`;
612
+ return n || e.getUTCHours() === 0 ? `${e.getUTCDate()} ${N[e.getUTCMonth()]} ${t}` : t;
613
+ }
614
+ case "day": return String(e.getUTCDate());
615
+ case "week": return `W${pe(e)}`;
616
+ case "month": {
617
+ let t = N[e.getUTCMonth()];
618
+ return n || e.getUTCMonth() === 0 ? `${t} ${e.getUTCFullYear()}` : t;
619
+ }
620
+ case "year": return String(e.getUTCFullYear());
621
+ }
622
+ }
623
+ function he(e, t, n) {
624
+ let r = [];
625
+ for (let i = de(e, n); i <= t && r.length < 1e3; i = fe(i, n)) r.push({
626
+ date: i,
627
+ at: P(i, e, t),
628
+ label: me(i, n, r.length === 0),
629
+ serial: F(i, n)
630
+ });
631
+ return r;
632
+ }
633
+ var I = 14, L = (e) => Math.min(1, Math.max(0, e)), R = (e) => `${(e * 100).toFixed(4)}%`;
634
+ function z(e, t, n) {
635
+ let r = document.createElement(e);
636
+ return r.className = t, n !== void 0 && (r.textContent = n), r;
637
+ }
638
+ function ge(e, t) {
639
+ e.classList.add("tlv"), e.classList.toggle("tlv--no-dates", !t.view.showDates);
640
+ let n = _e(t);
641
+ if (e.replaceChildren(...t.timelines.map((e) => Se(t, e, n.get(String(e.id)) ?? []))), e.append(...ve(t, n)), t.view.name.trim()) {
642
+ let n = z("div", "tlv-title", t.view.name);
643
+ n.setAttribute("role", "heading"), n.setAttribute("aria-level", "2"), e.prepend(n);
644
+ }
645
+ De(e), G(e), ye(e);
646
+ }
647
+ function B(e, t) {
648
+ let { start: n, finish: r } = e, i = L(P(t.start, n, r)), a = t.isMilestone ? i : L(P(t.finish, n, r)), o = !t.isMilestone && t.start < n, s = !t.isMilestone && t.finish > r;
649
+ return {
650
+ from: i,
651
+ to: a,
652
+ center: (i + a) / 2,
653
+ before: o,
654
+ after: s
655
+ };
656
+ }
657
+ function V(e, t, n, r) {
658
+ r <= 0 ? (e.style.left = "0", e.style.width = `${I}px`, e.classList.add(`${t}--outside-before`)) : n >= 1 ? (e.style.left = `calc(100% - ${I}px)`, e.style.width = `${I}px`, e.classList.add(`${t}--outside-after`)) : (e.style.left = R(n), e.style.width = R(r - n));
659
+ }
660
+ function H(e, t) {
661
+ t.color && e.style.setProperty(t.isMilestone ? "--tlv-milestone" : "--tlv-task", t.color);
662
+ }
663
+ function U(e, t, n, r) {
664
+ e.classList.toggle(`${t}--before`, n), e.classList.toggle(`${t}--after`, r);
665
+ }
666
+ function _e(e) {
667
+ let t = /* @__PURE__ */ new Map(), n = e.timelines, r = k(e);
668
+ for (let i of e.markers) {
669
+ if (e.view.absolute && r && L(P(i.date, r.start, r.finish)) !== P(i.date, r.start, r.finish)) continue;
670
+ let a = n.map((e, t) => i.global || e.markers.some((e) => String(e) === String(i.id)) ? t : -1).filter((e) => e >= 0);
671
+ if (a.length === 0) continue;
672
+ let o = i.offset.y > 0;
673
+ if (i.global && !e.view.absolute) {
674
+ for (let e of n) {
675
+ let n = P(i.date, e.start, e.finish);
676
+ if (n < 0 || n > 1) continue;
677
+ let r = t.get(String(e.id)) ?? [];
678
+ r.push({
679
+ marker: i,
680
+ at: n,
681
+ up: !1,
682
+ down: !1,
683
+ label: o ? "below" : "above"
684
+ }), t.set(String(e.id), r);
685
+ }
686
+ continue;
687
+ }
688
+ let s = [];
689
+ for (let t = Math.min(...a); t <= Math.max(...a); t++) {
690
+ let r = P(i.date, n[t].start, n[t].finish);
691
+ (e.view.absolute || r >= 0 && r <= 1) && s.push({
692
+ index: t,
693
+ at: r
694
+ });
695
+ }
696
+ if (s.length === 0) continue;
697
+ let c = s[0].index, l = s[s.length - 1].index;
698
+ for (let { index: e, at: r } of s) {
699
+ let a = t.get(String(n[e].id)) ?? [];
700
+ a.push({
701
+ marker: i,
702
+ at: r,
703
+ up: e !== c,
704
+ down: e !== l,
705
+ label: !o && e === c ? "above" : o && e === l ? "below" : void 0
706
+ }), t.set(String(n[e].id), a);
707
+ }
708
+ }
709
+ return t;
710
+ }
711
+ function W(e, t) {
712
+ return t.color && e.style.setProperty("--tlv-marker", t.color), e;
713
+ }
714
+ function ve(e, t) {
715
+ let n = [];
716
+ for (let r of e.markers) {
717
+ let i = e.timelines.flatMap((e) => {
718
+ let n = t.get(String(e.id))?.find((e) => e.marker === r);
719
+ return n ? [{
720
+ timeline: e,
721
+ part: n
722
+ }] : [];
723
+ }), a = [], o = () => {
724
+ if (a.length === 0) return;
725
+ let e = a[0], t = a[a.length - 1], i = W(z("span", "tlv-marker-line"), r);
726
+ Object.assign(i.dataset, {
727
+ marker: String(r.id),
728
+ from: String(e.timeline.id),
729
+ to: String(t.timeline.id),
730
+ at: String(e.part.at),
731
+ top: e.part.up ? "row" : "bar",
732
+ bottom: t.part.down ? "row" : "bar"
733
+ }), n.push(i), a = [];
734
+ };
735
+ for (let t of i) {
736
+ let n = a[a.length - 1];
737
+ n !== void 0 && n.part.down && t.part.up && e.timelines.indexOf(t.timeline) === e.timelines.indexOf(n.timeline) + 1 && (e.view.absolute || Math.abs(n.part.at - t.part.at) < 1e-9) || o(), a.push(t);
738
+ }
739
+ o();
740
+ }
741
+ return n;
742
+ }
743
+ function G(e) {
744
+ let t = [...e.querySelectorAll(":scope > .tlv-marker-line")];
745
+ if (t.length === 0) return;
746
+ let n = [...e.querySelectorAll(".tlv-timeline")], r = (e) => n.find((t) => t.dataset.timeline === e);
747
+ t[0].style.left = "0px", t[0].style.top = "0px";
748
+ let i = t[0].getBoundingClientRect();
749
+ for (let e of t) {
750
+ let t = r(e.dataset.from), n = r(e.dataset.to), a = t?.querySelector(".tlv-span")?.getBoundingClientRect(), o = t?.querySelector(".tlv-bar")?.getBoundingClientRect(), s = n?.querySelector(".tlv-bar")?.getBoundingClientRect();
751
+ if (!t || !n || !a || !o || !s) continue;
752
+ let c = a.left + Number(e.dataset.at) * a.width, l = e.dataset.top === "bar" ? o.top : t.getBoundingClientRect().top, u = e.dataset.bottom === "bar" ? s.bottom : n.getBoundingClientRect().bottom;
753
+ e.style.left = `${c - 1 - i.left}px`, e.style.top = `${l - i.top}px`, e.style.height = `${Math.max(0, u - l)}px`;
754
+ }
755
+ }
756
+ var K = /* @__PURE__ */ new WeakMap();
757
+ function ye(e) {
758
+ if (typeof ResizeObserver > "u") return;
759
+ let t = K.get(e);
760
+ if (t || (t = new ResizeObserver(() => G(e)), K.set(e, t)), t.disconnect(), e.querySelector(":scope > .tlv-marker-line")) {
761
+ t.observe(e);
762
+ for (let n of e.querySelectorAll(".tlv-timeline")) t.observe(n);
763
+ }
764
+ }
765
+ function be(e) {
766
+ K.get(e)?.disconnect(), K.delete(e);
767
+ }
768
+ function xe(e, t) {
769
+ let { marker: n, at: r } = e, { x: i, y: a } = n.offset, o = W(z("div", "tlv-connector tlv-connector--marker"), n);
770
+ o.style.left = R(r), o.style[t === "above" ? "bottom" : "top"] = "0";
771
+ let s = W(z("div", "tlv-marker-label"), n);
772
+ return Object.assign(s.dataset, {
773
+ entry: "",
774
+ marker: String(n.id),
775
+ at: String(r),
776
+ x: String(i),
777
+ y: String(a),
778
+ reach: "0"
779
+ }), s.style.marginLeft = `calc(${R(r)} + ${i}px)`, t === "above" ? s.style.marginBottom = `${-a}px` : s.style.marginTop = `${a}px`, s.title = `${n.text} (${m(n.date)})`, s.append(z("div", "tlv-callout-title", n.text), z("div", "tlv-callout-dates", m(n.date))), [o, s];
780
+ }
781
+ function Se(e, t, n) {
782
+ let r = z("section", `tlv-timeline tlv-timeline--name-${e.view.namePosition}`);
783
+ r.dataset.timeline = String(t.id), r.setAttribute("aria-label", t.name);
784
+ let i = z("div", "tlv-span"), a = k(e);
785
+ if (e.view.absolute && a) {
786
+ let e = P(t.start, a.start, a.finish), n = P(t.finish, a.start, a.finish);
787
+ i.style.marginLeft = R(e), i.style.width = R(n - e);
788
+ }
789
+ let o = Ee(e, t, "above"), s = we(e, t), c = Ee(e, t, "below");
790
+ i.append(o, s, c);
791
+ for (let e of n) e.label && (e.label === "above" ? o : c).append(...xe(e, e.label));
792
+ let l = z("div", "tlv-center");
793
+ return l.append(i), r.append(z("div", "tlv-name", t.name), l), r;
794
+ }
795
+ function q(e, t, n, r) {
796
+ let i = z("div", e);
797
+ return i.dataset.entry = "", i.dataset.timeline = String(t.id), i.dataset.task = String(n.taskId), i.dataset.at = String(r), i;
798
+ }
799
+ function J(e) {
800
+ return e.isMilestone ? m(e.start) : `${m(e.start)} – ${m(e.finish)}`;
801
+ }
802
+ function Ce(e, t, n) {
803
+ let r = z("div", `tlv-date tlv-date--${e}`);
804
+ return r.append(z("span", "tlv-date-caption", t), z("span", "tlv-date-value", m(n))), r;
805
+ }
806
+ function we(e, t) {
807
+ let { scale: n, showDates: r } = e.view, { start: i, finish: a } = t, o = z("div", "tlv-bar");
808
+ r && o.append(Ce("start", "Start", i), Ce("finish", "Finish", a));
809
+ let s = z("div", "tlv-scale"), c = he(i, a, n), l = [
810
+ {
811
+ at: 0,
812
+ serial: F(i, n)
813
+ },
814
+ ...c.filter((e) => e.at > 0 && e.at < 1).map((e) => ({
815
+ at: e.at,
816
+ serial: e.serial
817
+ })),
818
+ {
819
+ at: 1,
820
+ serial: 0
821
+ }
822
+ ];
823
+ for (let e = 0; e < l.length - 1; e++) {
824
+ let t = z("span", `tlv-period${l[e].serial % 2 ? " tlv-period--alt" : ""}`);
825
+ t.style.left = R(l[e].at), t.style.width = R(l[e + 1].at - l[e].at), s.append(t);
826
+ }
827
+ for (let e of c) {
828
+ if (e.at >= 1) continue;
829
+ let t = z("span", "tlv-tick", e.label);
830
+ t.style.left = R(e.at), s.append(t);
831
+ }
832
+ let u = z("div", "tlv-track"), d = (e, n) => {
833
+ let r = z("div", n ? "tlv-line tlv-line--new" : "tlv-line");
834
+ return r.dataset.timeline = String(t.id), r.dataset.placement = "on", r.dataset.line = String(e), n && (r.dataset.newLine = "true"), r.style.height = `${n ? 14 : 28}px`, r;
835
+ }, f = A(t), p = M(t);
836
+ u.append(d(f, !0));
837
+ for (let n = f; n < p; n++) {
838
+ let r = d(n, !1);
839
+ for (let i of t.entries) i.placement === "on" && i.line === n && r.append(Te(e, t, i));
840
+ u.append(r);
841
+ }
842
+ return u.append(d(p, !0)), o.append(s, u), o;
843
+ }
844
+ function Te(e, t, n) {
845
+ let r = e.findTask(n.taskId), { from: i, to: a, center: o, before: s, after: c } = B(t, r), l = `${r.text} (${J(r)})`;
846
+ if (r.isMilestone) {
847
+ let a = q("tlv-milestone", t, n, o);
848
+ return a.style.left = R(i), a.title = l, H(a, r), a.append(z("span", `tlv-symbol tlv-symbol--${r.symbol ?? e.view.milestoneSymbol}`), z("span", "tlv-milestone-label", r.text)), a;
849
+ }
850
+ let u = q("tlv-task", t, n, o);
851
+ return V(u, "tlv-task", i, a), u.title = l, H(u, r), u.append(z("span", "tlv-task-label", r.text)), U(u, "tlv-task", s, c), s && u.append(z("span", "tlv-arrow tlv-arrow--before")), c && u.append(z("span", "tlv-arrow tlv-arrow--after")), u;
852
+ }
853
+ function Ee(e, t, n) {
854
+ let r = z("div", `tlv-area tlv-area--${n}`);
855
+ r.dataset.timeline = String(t.id), r.dataset.placement = n;
856
+ for (let i of t.entries) {
857
+ if (i.placement !== n) continue;
858
+ let a = e.findTask(i.taskId), { from: o, to: s, center: c, before: l, after: u } = B(t, a), { x: d, y: f } = i.calloutOffset, p, m;
859
+ a.isMilestone ? (p = z("span", `tlv-edge-symbol tlv-symbol tlv-symbol--${a.symbol ?? e.view.milestoneSymbol}`), p.style.left = R(c), m = 8) : (p = z("span", "tlv-edge-bar"), V(p, "tlv-edge-bar", o, s), U(p, "tlv-edge-bar", l, u), m = 6), H(p, a);
860
+ let h = z("div", "tlv-connector");
861
+ h.style.left = R(c), h.style[n === "above" ? "bottom" : "top"] = `${m}px`;
862
+ let g = q("tlv-callout", t, i, c);
863
+ Object.assign(g.dataset, {
864
+ x: String(d),
865
+ y: String(f),
866
+ reach: String(m)
867
+ }), g.style.marginLeft = `calc(${R(c)} + ${d}px)`, n === "above" ? g.style.marginBottom = `${-f}px` : g.style.marginTop = `${f}px`, g.title = `${a.text} (${J(a)})`, g.append(z("div", "tlv-callout-title", a.text), z("div", "tlv-callout-dates", J(a))), r.append(p, h, g);
868
+ }
869
+ return r;
870
+ }
871
+ function De(e) {
872
+ for (let t of e.querySelectorAll(".tlv-callout, .tlv-marker-label")) {
873
+ let e = t.previousElementSibling, n = Number(t.dataset.x), r = Number(t.dataset.y), i = Number(t.dataset.reach), a = t.parentElement.classList.contains("tlv-area--above") ? r + i : r - i, o = Math.min(Math.max(0, n), n + t.offsetWidth), s = Math.hypot(o, a);
874
+ e.style.width = `${s}px`, e.style.transform = `rotate(${Math.atan2(a, o)}rad)`, e.hidden = s < 1;
875
+ }
876
+ }
877
+ // src/svg.ts
878
+ var Oe = "http://www.w3.org/2000/svg", ke = {
879
+ diamond: [
880
+ [7, -.92],
881
+ [14.92, 7],
882
+ [7, 14.92],
883
+ [-.92, 7]
884
+ ],
885
+ square: [
886
+ [1.05, 1.05],
887
+ [12.95, 1.05],
888
+ [12.95, 12.95],
889
+ [1.05, 12.95]
890
+ ],
891
+ triangle: [
892
+ [7, 0],
893
+ [14, 14],
894
+ [0, 14]
895
+ ],
896
+ star: [
897
+ [50, 0],
898
+ [61, 35],
899
+ [98, 35],
900
+ [68, 57],
901
+ [79, 91],
902
+ [50, 70],
903
+ [21, 91],
904
+ [32, 57],
905
+ [2, 35],
906
+ [39, 35]
907
+ ].map(([e, t]) => [e * 14 / 100, t * 14 / 100])
908
+ }, Ae = (e) => e.replace(/[&<>"']/g, (e) => ({
909
+ "&": "&amp;",
910
+ "<": "&lt;",
911
+ ">": "&gt;",
912
+ "\"": "&quot;",
913
+ "'": "&apos;"
914
+ })[e]), Y = (e) => String(Math.round(e * 100) / 100), X = (e) => e !== "" && e !== "transparent" && !/rgba\(.*,\s*0\)$/.test(e), je = class {
915
+ root;
916
+ parts = [];
917
+ clipCount = 0;
918
+ origin;
919
+ constructor(e) {
920
+ this.root = e;
921
+ let t = e.getBoundingClientRect();
922
+ this.origin = {
923
+ x: t.left + e.clientLeft - e.scrollLeft,
924
+ y: t.top + e.clientTop - e.scrollTop
925
+ };
926
+ }
927
+ box(e) {
928
+ return this.fromRect(e.getBoundingClientRect());
929
+ }
930
+ fromRect(e) {
931
+ return {
932
+ x: e.left - this.origin.x,
933
+ y: e.top - this.origin.y,
934
+ width: e.width,
935
+ height: e.height
936
+ };
937
+ }
938
+ add(e) {
939
+ this.parts.push(e);
940
+ }
941
+ rect(e, t, n = "") {
942
+ e.width <= 0 || e.height <= 0 || this.add(`<rect x="${Y(e.x)}" y="${Y(e.y)}" width="${Y(e.width)}" height="${Y(e.height)}" fill="${t}"${n}/>`);
943
+ }
944
+ clip(e, t = 0) {
945
+ let n = `tlv-clip-${++this.clipCount}`;
946
+ return this.add(`<clipPath id="${n}"><rect x="${Y(e.x)}" y="${Y(e.y)}" width="${Y(e.width)}" height="${Y(e.height)}" rx="${Y(t)}"/></clipPath>`), n;
947
+ }
948
+ text(e, t) {
949
+ if (!e?.textContent) return;
950
+ let n = getComputedStyle(e), r = document.createRange();
951
+ r.selectNodeContents(e);
952
+ let i = typeof r.getBoundingClientRect == "function" ? r.getBoundingClientRect() : void 0, a = i && i.width > 0 ? this.fromRect(i) : this.box(e), o = (n.writingMode ?? "").startsWith("vertical"), s = a.x + a.width / 2, c = a.y + a.height / 2, l = ` font-family="${Ae(n.fontFamily)}" font-size="${n.fontSize}"` + (Number(n.fontWeight) >= 600 || n.fontWeight === "bold" ? ` font-weight="${n.fontWeight}"` : ""), u;
953
+ if (o) {
954
+ let t = e.closest(".tlv-timeline--name-left") ? -90 : 90;
955
+ u = `x="${Y(s)}" y="${Y(c)}" text-anchor="middle" transform="rotate(${t} ${Y(s)} ${Y(c)})"`;
956
+ } else u = `x="${Y(a.x)}" y="${Y(c)}"`;
957
+ let d = `<text ${u} dominant-baseline="central" fill="${n.color}"${l}>${Ae(e.textContent)}</text>`;
958
+ this.add(t ? `<g clip-path="url(#${this.clip(t)})">${d}</g>` : d);
959
+ }
960
+ size() {
961
+ let e = 0, t = 0;
962
+ for (let n of this.root.querySelectorAll(".tlv-title, .tlv-timeline")) {
963
+ let r = this.box(n);
964
+ e = Math.max(e, r.x + r.width), t = Math.max(t, r.y + r.height);
965
+ }
966
+ return {
967
+ width: Math.ceil(e),
968
+ height: Math.ceil(t)
969
+ };
970
+ }
971
+ toString() {
972
+ let { width: e, height: t } = this.size();
973
+ return `<svg xmlns="${Oe}" width="${Y(e)}" height="${Y(t)}" viewBox="0 0 ${Y(e)} ${Y(t)}">` + this.parts.join("") + "</svg>";
974
+ }
975
+ };
976
+ function Me(e, t) {
977
+ let { x: n, y: r, width: i, height: a } = e, o = Math.min(i, a) / 2, s = (e) => Math.min(parseFloat(e) || 0, o), [c, l, u, d] = [
978
+ s(t.borderTopLeftRadius),
979
+ s(t.borderTopRightRadius),
980
+ s(t.borderBottomRightRadius),
981
+ s(t.borderBottomLeftRadius)
982
+ ], f = (e, t, n) => e ? `A${Y(e)} ${Y(e)} 0 0 1 ${Y(t)} ${Y(n)}` : `L${Y(t)} ${Y(n)}`;
983
+ return `M${Y(n + c)} ${Y(r)}H${Y(n + i - l)}${f(l, n + i, r + l)}V${Y(r + a - u)}${f(u, n + i - u, r + a)}H${Y(n + d)}${f(d, n, r + a - d)}V${Y(r + c)}${f(c, n + c, r)}Z`;
984
+ }
985
+ function Ne(e, t, n) {
986
+ let { x: r, y: i, width: a, height: o } = e, s = Math.min(6, a / 2);
987
+ return `M${[
988
+ [t ? r + s : r, i],
989
+ [n ? r + a - s : r + a, i],
990
+ ...n ? [[r + a, i + o / 2]] : [],
991
+ [n ? r + a - s : r + a, i + o],
992
+ [t ? r + s : r, i + o],
993
+ ...t ? [[r, i + o / 2]] : []
994
+ ].map(([e, t]) => `${Y(e)} ${Y(t)}`).join("L")}Z`;
995
+ }
996
+ function Pe(e, t) {
997
+ let { x: n, y: r, width: i, height: a } = e.box(t), o = t.classList.contains("tlv-arrow--before") ? [
998
+ [n + i, r],
999
+ [n, r + a / 2],
1000
+ [n + i, r + a]
1001
+ ] : [
1002
+ [n, r],
1003
+ [n + i, r + a / 2],
1004
+ [n, r + a]
1005
+ ];
1006
+ e.add(`<polygon points="${o.map(([e, t]) => `${Y(e)},${Y(t)}`).join(" ")}" fill="${getComputedStyle(t).backgroundColor}"/>`);
1007
+ }
1008
+ function Fe(e, t) {
1009
+ let n = e.box(t), r = n.x + n.width / 2, i = n.y + n.height / 2, a = getComputedStyle(t).backgroundColor, o = [...t.classList].find((e) => e.startsWith("tlv-symbol--"))?.slice(12) ?? "diamond";
1010
+ if (o === "circle") {
1011
+ e.add(`<circle cx="${Y(r)}" cy="${Y(i)}" r="7" fill="${a}"/>`);
1012
+ return;
1013
+ }
1014
+ let s = (ke[o] ?? ke.diamond).map(([e, t]) => `${Y(r - 7 + e)},${Y(i - 7 + t)}`).join(" ");
1015
+ e.add(`<polygon points="${s}" fill="${a}"/>`);
1016
+ }
1017
+ function Ie(e, t) {
1018
+ let n = getComputedStyle(t), r = e.box(t), i = parseFloat(n.borderTopLeftRadius) || 0;
1019
+ e.rect(r, n.backgroundColor, ` rx="${Y(i)}"`);
1020
+ let a = t.querySelector(".tlv-scale"), o = e.clip(r, i);
1021
+ e.add(`<g clip-path="url(#${o})">`);
1022
+ for (let t of a.querySelectorAll(".tlv-period")) e.rect(e.box(t), getComputedStyle(t).backgroundColor);
1023
+ let s = e.box(a), c = getComputedStyle(a);
1024
+ ((t, n) => e.add(`<line x1="${Y(s.x)}" y1="${Y(t)}" x2="${Y(s.x + s.width)}" y2="${Y(t)}" stroke="${n}"/>`))(s.y + s.height - .5, c.borderBottomColor), e.add("</g>");
1025
+ for (let t of a.querySelectorAll(".tlv-tick")) e.text(t, s);
1026
+ for (let n of t.querySelectorAll(".tlv-task")) {
1027
+ let t = e.box(n), r = getComputedStyle(n);
1028
+ e.add(`<path d="${Me(t, r)}" fill="${r.backgroundColor}"/>`);
1029
+ let i = n.querySelector(".tlv-task-label");
1030
+ e.text(i, getComputedStyle(i).position === "absolute" ? void 0 : t);
1031
+ for (let t of n.querySelectorAll(".tlv-arrow")) Pe(e, t);
1032
+ }
1033
+ for (let n of t.querySelectorAll(".tlv-milestone")) Fe(e, n.querySelector(".tlv-symbol")), e.text(n.querySelector(".tlv-milestone-label"));
1034
+ e.rect({
1035
+ x: r.x + .5,
1036
+ y: r.y + .5,
1037
+ width: r.width - 1,
1038
+ height: r.height - 1
1039
+ }, "none", ` rx="${Y(i)}" stroke="${n.borderTopColor}"`);
1040
+ for (let n of t.querySelectorAll(".tlv-date")) e.text(n.querySelector(".tlv-date-caption")), e.text(n.querySelector(".tlv-date-value"));
1041
+ }
1042
+ function Le(e, t) {
1043
+ let n = e.box(t), r = t.classList.contains("tlv-area--above"), i = r ? n.y + n.height : n.y, a = (t, a) => {
1044
+ if (a.hidden) return;
1045
+ let o = Number(t.dataset.x), s = Number(t.dataset.y), c = Number(t.dataset.reach), l = n.x + Number(t.dataset.at) * n.width, u = r ? i - c : i + c, d = Math.min(Math.max(0, o), o + t.offsetWidth), f = r ? s + c : s - c, p = getComputedStyle(a), m = a.classList.contains("tlv-connector--marker") ? "" : " stroke-dasharray=\"3 3\"";
1046
+ e.add(`<line x1="${Y(l)}" y1="${Y(u)}" x2="${Y(l + d)}" y2="${Y(u + f)}" stroke="${p.borderTopColor}"${m}/>`);
1047
+ };
1048
+ for (let n of t.querySelectorAll(".tlv-marker-label")) a(n, n.previousElementSibling), Re(e, n);
1049
+ for (let n of t.querySelectorAll(".tlv-callout")) {
1050
+ let t = n.previousElementSibling, r = t.previousElementSibling;
1051
+ if (a(n, t), r.classList.contains("tlv-edge-bar")) {
1052
+ let t = getComputedStyle(r), n = r.classList.contains("tlv-edge-bar--before"), i = r.classList.contains("tlv-edge-bar--after"), a = e.box(r), o = n || i ? Ne(a, n, i) : Me(a, t);
1053
+ e.add(`<path d="${o}" fill="${t.backgroundColor}"/>`);
1054
+ } else Fe(e, r);
1055
+ Re(e, n);
1056
+ }
1057
+ }
1058
+ function Re(e, t) {
1059
+ let n = getComputedStyle(t), r = e.box(t), i = parseFloat(n.borderTopLeftRadius) || 0;
1060
+ X(n.backgroundColor) && e.rect(r, n.backgroundColor, ` rx="${Y(i)}"`), e.rect({
1061
+ x: r.x + .5,
1062
+ y: r.y + .5,
1063
+ width: r.width - 1,
1064
+ height: r.height - 1
1065
+ }, "none", ` rx="${Y(i)}" stroke="${n.borderTopColor}"`), e.text(t.querySelector(".tlv-callout-title"), r), e.text(t.querySelector(".tlv-callout-dates"), r);
1066
+ }
1067
+ function ze(e) {
1068
+ let t = new je(e), n = getComputedStyle(e), { width: r, height: i } = t.size();
1069
+ X(n.backgroundColor) && t.rect({
1070
+ x: 0,
1071
+ y: 0,
1072
+ width: r,
1073
+ height: i
1074
+ }, n.backgroundColor);
1075
+ let a = e.querySelector(".tlv-title");
1076
+ if (a) {
1077
+ let e = t.box(a), n = getComputedStyle(a);
1078
+ if (X(n.backgroundColor) && t.rect(e, n.backgroundColor), t.text(a), parseFloat(n.borderBottomWidth) > 0) {
1079
+ let r = e.y + e.height - .5;
1080
+ t.add(`<line x1="${Y(e.x)}" y1="${Y(r)}" x2="${Y(e.x + e.width)}" y2="${Y(r)}" stroke="${n.borderBottomColor}"/>`);
1081
+ }
1082
+ }
1083
+ for (let n of e.querySelectorAll(".tlv-timeline")) {
1084
+ let e = getComputedStyle(n), r = t.box(n), i = n.querySelector(".tlv-name"), a = t.box(i);
1085
+ if (t.rect(a, getComputedStyle(i).backgroundColor), t.text(i, a), parseFloat(e.borderBottomWidth) > 0) {
1086
+ let n = r.y + r.height - .5;
1087
+ t.add(`<line x1="${Y(r.x)}" y1="${Y(n)}" x2="${Y(r.x + r.width)}" y2="${Y(n)}" stroke="${e.borderBottomColor}"/>`);
1088
+ }
1089
+ Ie(t, n.querySelector(".tlv-bar"));
1090
+ }
1091
+ for (let n of e.querySelectorAll(":scope > .tlv-marker-line")) t.rect(t.box(n), getComputedStyle(n).backgroundColor);
1092
+ for (let n of e.querySelectorAll(".tlv-timeline")) for (let e of n.querySelectorAll(".tlv-area")) Le(t, e);
1093
+ return t.toString();
1094
+ }
1095
+ // src/view.ts
1096
+ var Be = [
1097
+ "onTaskClick",
1098
+ "onTimelineClick",
1099
+ "onTaskSelected",
1100
+ "onTimelineSelected"
1101
+ ], Z = class extends Error {
1102
+ constructor() {
1103
+ super("Move cancelled because an earlier move failed"), this.name = "MoveCancelledError";
1104
+ }
1105
+ }, Ve = (e) => typeof e == "object" && !!e && "view" in e && "timelines" in e, He = class {
1106
+ container;
1107
+ model;
1108
+ api;
1109
+ listeners = /* @__PURE__ */ new Map();
1110
+ handlers = /* @__PURE__ */ new Map();
1111
+ lastEventId = 0;
1112
+ selected = {
1113
+ kind: "task",
1114
+ ids: []
1115
+ };
1116
+ detachDrag;
1117
+ queue = Promise.resolve();
1118
+ pending = 0;
1119
+ generation = 0;
1120
+ constructor(e, t = {}) {
1121
+ let r = typeof e == "string" ? document.getElementById(e) : e;
1122
+ if (!r) throw Error(`Timeline container "${e}" not found`);
1123
+ this.container = r, t.api_url && (this.api = new n(t.api_url, t.fetch)), this.detachDrag = u(r, (e) => this.onDrop(e), (e, t) => this.onEntryClick(e, t), (e, t) => this.onBackgroundClick(e, t), (e) => this.onMarkerDrop(e));
1124
+ }
1125
+ parse(e) {
1126
+ this.model = O.fromData(e), this.render(), this.dropStaleSelection();
1127
+ }
1128
+ async load(e) {
1129
+ if (!this.api) throw Error("load() needs the api_url option");
1130
+ this.parse(await this.api.getView(e));
1131
+ }
1132
+ toSVG() {
1133
+ this.requireModel();
1134
+ let e = this.selected;
1135
+ this.selected = {
1136
+ kind: "task",
1137
+ ids: []
1138
+ }, this.applySelection();
1139
+ try {
1140
+ return ze(this.container);
1141
+ } finally {
1142
+ this.selected = e, this.applySelection();
1143
+ }
1144
+ }
1145
+ serialize() {
1146
+ return this.requireModel().toData();
1147
+ }
1148
+ on(e, t) {
1149
+ let n = this.listeners.get(e);
1150
+ return n || this.listeners.set(e, n = /* @__PURE__ */ new Set()), n.add(t), () => n.delete(t);
1151
+ }
1152
+ attachEvent(e, t) {
1153
+ if (!Be.includes(e)) throw Error(`Unknown event "${e}"`);
1154
+ if (typeof t != "function") throw Error("attachEvent needs a handler function");
1155
+ let n = ++this.lastEventId;
1156
+ return this.handlers.set(n, {
1157
+ name: e,
1158
+ handler: t
1159
+ }), n;
1160
+ }
1161
+ detachEvent(e) {
1162
+ return this.handlers.delete(e);
1163
+ }
1164
+ getSelectedTasks() {
1165
+ return this.selected.kind === "task" ? [...this.selected.ids] : [];
1166
+ }
1167
+ getSelectedTimelines() {
1168
+ return this.selected.kind === "timeline" ? [...this.selected.ids] : [];
1169
+ }
1170
+ selectTask(e, t = !1) {
1171
+ let n = this.requireModel().findTask(e);
1172
+ if (!n) throw Error(`Task ${e} not found`);
1173
+ return this.select("task", n.id, t);
1174
+ }
1175
+ selectTimeline(e, t = !1) {
1176
+ let n = this.requireModel().findTimeline(e);
1177
+ if (!n) throw Error(`Timeline ${e} not found`);
1178
+ return this.select("timeline", n.id, t);
1179
+ }
1180
+ unselectTask(e) {
1181
+ return this.unselect("task", e);
1182
+ }
1183
+ unselectTimeline(e) {
1184
+ return this.unselect("timeline", e);
1185
+ }
1186
+ clearSelection() {
1187
+ this.setSelection(this.selected.kind, []);
1188
+ }
1189
+ setMarkerOffset(e, t) {
1190
+ let n = this.requireModel().setMarkerOffset(e, t);
1191
+ this.render(), this.emit("marker_moved", {
1192
+ marker_id: n.id,
1193
+ marker_offset: { ...n.offset }
1194
+ });
1195
+ }
1196
+ moveTimelineUp(e) {
1197
+ return this.moveTimeline(e, -1);
1198
+ }
1199
+ moveTimelineDown(e) {
1200
+ return this.moveTimeline(e, 1);
1201
+ }
1202
+ moveTimeline(e, t) {
1203
+ let n = this.requireModel().moveTimeline(e, t);
1204
+ return n && this.render(), n;
1205
+ }
1206
+ async moveEntry(t, n, r) {
1207
+ let i = this.requireModel(), a = {
1208
+ timeline_id: t,
1209
+ task_id: n,
1210
+ patch: r
1211
+ }, o = i.toData();
1212
+ try {
1213
+ i.moveEntry(t, n, ue(r));
1214
+ } catch (e) {
1215
+ throw this.emit("move_failed", {
1216
+ ...a,
1217
+ error: e
1218
+ }), e;
1219
+ }
1220
+ if (this.render(), this.emit("entry_moved", a), !this.api) return;
1221
+ let s = i.view.id;
1222
+ if (s === void 0) {
1223
+ let e = /* @__PURE__ */ Error("view.id is required when api_url is set");
1224
+ throw this.rollback(o), this.emit("move_failed", {
1225
+ ...a,
1226
+ error: e
1227
+ }), e;
1228
+ }
1229
+ let c = this.api, l = this.generation;
1230
+ this.pending++;
1231
+ let u = this.queue.then(async () => {
1232
+ try {
1233
+ if (l !== this.generation) throw new Z();
1234
+ let e = await c.moveEntry(s, t, n, r, this.model?.version);
1235
+ if (l !== this.generation) return;
1236
+ this.model.version = e.version, this.pending === 1 && (this.model.replaceTimelines(e.timelines), this.render());
1237
+ } catch (t) {
1238
+ throw t instanceof Z || (this.generation++, t instanceof e && t.status === 409 && Ve(t.body) ? this.parse(t.body) : this.rollback(o), this.emit("move_failed", {
1239
+ ...a,
1240
+ error: t
1241
+ })), t;
1242
+ } finally {
1243
+ this.pending--;
1244
+ }
1245
+ });
1246
+ return this.queue = u.catch(() => void 0), u;
1247
+ }
1248
+ destroy() {
1249
+ this.detachDrag(), be(this.container), this.listeners.clear(), this.handlers.clear(), this.container.replaceChildren(), this.container.classList.remove("tlv");
1250
+ }
1251
+ rollback(e) {
1252
+ let t = this.model?.version;
1253
+ this.model = O.fromData(e), this.model.version = t, this.render();
1254
+ }
1255
+ onDrop({ timeline: e, task: t, target: n, offset: r }) {
1256
+ let i = this.requireModel(), a = i.timelines.find((t) => String(t.id) === e), o = i.timelines.find((e) => String(e.id) === n.timeline), s = a?.entries.find((e) => String(e.taskId) === t);
1257
+ if (!a || !o || !s) return;
1258
+ let c = {};
1259
+ o !== a && (c.timeline_id = o.id), n.placement !== s.placement && (c.placement = n.placement);
1260
+ let l = c.timeline_id !== void 0 || c.placement !== void 0;
1261
+ if (n.placement === "on") n.newLine ? (c.line = n.line, c.new_line = !0) : (l || s.placement !== "on" || s.line !== n.line) && (c.line = n.line);
1262
+ else if (r) {
1263
+ let e = s.placement === "on" ? void 0 : s.calloutOffset;
1264
+ (l || !e || e.x !== r.x || e.y !== r.y) && (c.callout_offset = r);
1265
+ }
1266
+ Object.keys(c).length !== 0 && this.moveEntry(a.id, s.taskId, c).catch(() => void 0);
1267
+ }
1268
+ callHandlers(e, ...t) {
1269
+ for (let { name: n, handler: r } of [...this.handlers.values()]) n === e && r.apply(this, t);
1270
+ }
1271
+ onMarkerDrop({ marker: e, offset: t }) {
1272
+ let n = this.model?.markers.find((t) => String(t.id) === e);
1273
+ !n || n.offset.x === t.x && n.offset.y === t.y || this.setMarkerOffset(n.id, t);
1274
+ }
1275
+ onEntryClick(e, t) {
1276
+ let n = this.model, r = n?.tasks.find((t) => String(t.id) === e.dataset.task), i = n?.timelines.find((t) => String(t.id) === e.dataset.timeline);
1277
+ n && r && i && (this.callHandlers("onTaskClick", r.id, t, i.id), this.model === n && this.clickSelect("task", r.id, t));
1278
+ }
1279
+ onBackgroundClick(e, t) {
1280
+ let n = this.model;
1281
+ if (!n) return;
1282
+ let r = e.closest(".tlv-timeline"), i = r ? n.timelines.find((e) => String(e.id) === r.dataset.timeline) : void 0;
1283
+ i && this.callHandlers("onTimelineClick", i.id, t), this.model === n && (i ? this.clickSelect("timeline", i.id, t) : n.view.selection && this.clearSelection());
1284
+ }
1285
+ clickSelect(e, t, n) {
1286
+ if (!this.requireModel().view.selection) return;
1287
+ let r = this.isSelected(e, t);
1288
+ n.ctrlKey || n.metaKey ? r ? this.unselect(e, t) : this.select(e, t, !0) : r ? this.clearSelection() : this.select(e, t, !1);
1289
+ }
1290
+ isSelected(e, t) {
1291
+ return this.selected.kind === e && this.selected.ids.some((e) => String(e) === String(t));
1292
+ }
1293
+ select(e, t, n) {
1294
+ if (!this.requireModel().view.selection) return !1;
1295
+ let r = n && this.selected.kind === e ? this.selected.ids : [];
1296
+ return r.some((e) => String(e) === String(t)) || this.setSelection(e, [...r, t]), !0;
1297
+ }
1298
+ unselect(e, t) {
1299
+ return this.isSelected(e, t) ? (this.setSelection(e, this.selected.ids.filter((e) => String(e) !== String(t))), !0) : !1;
1300
+ }
1301
+ setSelection(e, t) {
1302
+ let n = this.selected;
1303
+ this.selected = {
1304
+ kind: e,
1305
+ ids: t
1306
+ }, this.applySelection();
1307
+ let r = (e, t) => t.kind === e ? t.ids : [];
1308
+ for (let e of ["task", "timeline"]) r(e, n).map(String).join("\0") !== r(e, this.selected).map(String).join("\0") && this.callHandlers(e === "task" ? "onTaskSelected" : "onTimelineSelected", [...r(e, this.selected)]);
1309
+ }
1310
+ dropStaleSelection() {
1311
+ let e = this.requireModel(), { kind: t, ids: n } = this.selected, r = e.view.selection ? n.filter((n) => (t === "task" ? e.findTask(n) : e.findTimeline(n)) !== void 0) : [];
1312
+ r.length !== n.length && this.setSelection(t, r);
1313
+ }
1314
+ applySelection() {
1315
+ let e = new Set(this.selected.ids.map(String)), t = this.selected.kind === "task";
1316
+ for (let n of this.container.querySelectorAll("[data-entry]")) n.classList.toggle("tlv-selected", t && e.has(n.dataset.task));
1317
+ for (let n of this.container.querySelectorAll(".tlv-timeline")) {
1318
+ let r = !t && e.has(n.dataset.timeline);
1319
+ n.classList.toggle("tlv-timeline--selected", r), n.setAttribute("aria-selected", String(r));
1320
+ }
1321
+ }
1322
+ emit(e, t) {
1323
+ for (let n of this.listeners.get(e) ?? []) n(t);
1324
+ }
1325
+ requireModel() {
1326
+ if (!this.model) throw Error("No data yet: call parse() or load() first");
1327
+ return this.model;
1328
+ }
1329
+ render() {
1330
+ ge(this.container, this.requireModel()), this.applySelection();
1331
+ }
1332
+ }, Q;
1333
+ function $() {
1334
+ if (!Q) throw Error("Call timeline.init(container) first");
1335
+ return Q;
1336
+ }
1337
+ function Ue(e, t) {
1338
+ return Q?.destroy(), Q = new He(e, t), Q;
1339
+ }
1340
+ var We = (e) => $().parse(e), Ge = (e) => $().load(e), Ke = () => $().serialize(), qe = () => $().toSVG(), Je = (e, t, n) => $().moveEntry(e, t, n), Ye = (e, t) => $().attachEvent(e, t), Xe = (e) => $().detachEvent(e), Ze = (e, t) => $().selectTask(e, t), Qe = (e, t) => $().selectTimeline(e, t), $e = (e) => $().unselectTask(e), et = (e) => $().unselectTimeline(e), tt = () => $().clearSelection(), nt = () => $().getSelectedTasks(), rt = () => $().getSelectedTimelines(), it = (e, t) => $().setMarkerOffset(e, t), at = (e) => $().moveTimelineUp(e), ot = (e) => $().moveTimelineDown(e), st = (e, t) => $().moveTimeline(e, t), ct = (e, t) => $().on(e, t);
1341
+ export { e as ApiError, Z as MoveCancelledError, S as MoveError, n as RestClient, O as TimelineModel, He as TimelineView, b as addDuration, Ye as attachEvent, tt as clearSelection, Xe as detachEvent, A as firstLine, nt as getSelectedTasks, rt as getSelectedTimelines, Ue as init, M as lineCount, Ge as load, Je as moveEntry, st as moveTimeline, ot as moveTimelineDown, at as moveTimelineUp, ct as on, We as parse, y as parseDuration, ue as patchFromData, Ze as selectTask, Qe as selectTimeline, Ke as serialize, it as setMarkerOffset, le as timelineToData, qe as toSVG, j as trimLines, $e as unselectTask, et as unselectTimeline };