diff --git a/dist/frappe-gantt.es.js b/dist/frappe-gantt.es.js index 5dc8c3b..693afdf 100644 --- a/dist/frappe-gantt.es.js +++ b/dist/frappe-gantt.es.js @@ -1,4 +1,4 @@ -const k = "year", D = "month", $ = "day", E = "hour", Y = "minute", A = "second", L = "millisecond", S = { +const k = "year", D = "month", $ = "day", Y = "hour", E = "minute", A = "second", L = "millisecond", S = { January: "Jan", February: "Feb", March: "Mar", @@ -12,8 +12,8 @@ const k = "year", D = "month", $ = "day", E = "hour", Y = "minute", A = "second" November: "Nov", December: "Dec" }, h = { - parse_duration(n) { - const e = /([0-9]+)(y|m|d|h|min|s|ms)/gm.exec(n); + parse_duration(r) { + const e = /([0-9]+)(y|m|d|h|min|s|ms)/gm.exec(r); if (e !== null) { if (e[2] === "y") return { duration: parseInt(e[1]), scale: "year" }; @@ -31,55 +31,55 @@ const k = "year", D = "month", $ = "day", E = "hour", Y = "minute", A = "second" return { duration: parseInt(e[1]), scale: "millisecond" }; } }, - parse(n, t = "-", e = /[.:]/) { - if (n instanceof Date) - return n; - if (typeof n == "string") { + parse(r, t = "-", e = /[.:]/) { + if (r instanceof Date) + return r; + if (typeof r == "string") { let s, i; - const o = n.split(" "); + const o = r.split(" "); s = o[0].split(t).map((a) => parseInt(a, 10)), i = o[1] && o[1].split(e), s[1] = s[1] ? s[1] - 1 : 0; - let r = s; - return i && i.length && (i.length === 4 && (i[3] = "0." + i[3], i[3] = parseFloat(i[3]) * 1e3), r = r.concat(i)), new Date(...r); + let n = s; + return i && i.length && (i.length === 4 && (i[3] = "0." + i[3], i[3] = parseFloat(i[3]) * 1e3), n = n.concat(i)), new Date(...n); } }, - to_string(n, t = !1) { - if (!(n instanceof Date)) + to_string(r, t = !1) { + if (!(r instanceof Date)) throw new TypeError("Invalid argument type"); - const e = this.get_date_values(n).map((o, r) => (r === 1 && (o = o + 1), r === 6 ? v(o + "", 3, "0") : v(o + "", 2, "0"))), s = `${e[0]}-${e[1]}-${e[2]}`, i = `${e[3]}:${e[4]}:${e[5]}.${e[6]}`; + const e = this.get_date_values(r).map((o, n) => (n === 1 && (o = o + 1), n === 6 ? v(o + "", 3, "0") : v(o + "", 2, "0"))), s = `${e[0]}-${e[1]}-${e[2]}`, i = `${e[3]}:${e[4]}:${e[5]}.${e[6]}`; return s + (t ? " " + i : ""); }, - format(n, t = "YYYY-MM-DD HH:mm:ss.SSS", e = "en") { + format(r, t = "YYYY-MM-DD HH:mm:ss.SSS", e = "en") { const i = new Intl.DateTimeFormat(e, { month: "long" - }).format(n), o = i.charAt(0).toUpperCase() + i.slice(1), r = this.get_date_values(n).map((g) => v(g, 2, 0)), a = { - YYYY: r[0], - MM: v(+r[1] + 1, 2, 0), - DD: r[2], - HH: r[3], - mm: r[4], - ss: r[5], - SSS: r[6], - D: r[2], + }).format(r), o = i.charAt(0).toUpperCase() + i.slice(1), n = this.get_date_values(r).map((l) => v(l, 2, 0)), a = { + YYYY: n[0], + MM: v(+n[1] + 1, 2, 0), + DD: n[2], + HH: n[3], + mm: n[4], + ss: n[5], + SSS: n[6], + D: n[2], MMMM: o, MMM: S[o] }; let p = t; const d = []; - return Object.keys(a).sort((g, c) => c.length - g.length).forEach((g) => { - p.includes(g) && (p = p.replaceAll(g, `$${d.length}`), d.push(a[g])); - }), d.forEach((g, c) => { - p = p.replaceAll(`$${c}`, g); + return Object.keys(a).sort((l, g) => g.length - l.length).forEach((l) => { + p.includes(l) && (p = p.replaceAll(l, `$${d.length}`), d.push(a[l])); + }), d.forEach((l, g) => { + p = p.replaceAll(`$${g}`, l); }), p; }, - diff(n, t, e = $) { - let s, i, o, r, a, p, d; - s = n - t, i = s / 1e3, r = i / 60, o = r / 60, a = o / 24; - const g = n.getFullYear() - t.getFullYear(), c = n.getMonth() - t.getMonth(); - return p = g * 12 + c, n.getDate() < t.getDate() && p--, d = p / 12, e.endsWith("s") || (e += "s"), Math.floor( + diff(r, t, e = $) { + let s, i, o, n, a, p, d; + s = r - t, i = s / 1e3, n = i / 60, o = n / 60, a = o / 24; + const l = r.getFullYear() - t.getFullYear(), g = r.getMonth() - t.getMonth(); + return p = l * 12 + g, r.getDate() < t.getDate() && p--, d = p / 12, e.endsWith("s") || (e += "s"), Math.floor( { milliseconds: s, seconds: i, - minutes: r, + minutes: n, hours: o, days: a, months: p, @@ -88,103 +88,103 @@ const k = "year", D = "month", $ = "day", E = "hour", Y = "minute", A = "second" ); }, today() { - const n = this.get_date_values(/* @__PURE__ */ new Date()).slice(0, 3); - return new Date(...n); + const r = this.get_date_values(/* @__PURE__ */ new Date()).slice(0, 3); + return new Date(...r); }, now() { return /* @__PURE__ */ new Date(); }, - add(n, t, e) { + add(r, t, e) { t = parseInt(t, 10); const s = [ - n.getFullYear() + (e === k ? t : 0), - n.getMonth() + (e === D ? t : 0), - n.getDate() + (e === $ ? t : 0), - n.getHours() + (e === E ? t : 0), - n.getMinutes() + (e === Y ? t : 0), - n.getSeconds() + (e === A ? t : 0), - n.getMilliseconds() + (e === L ? t : 0) + r.getFullYear() + (e === k ? t : 0), + r.getMonth() + (e === D ? t : 0), + r.getDate() + (e === $ ? t : 0), + r.getHours() + (e === Y ? t : 0), + r.getMinutes() + (e === E ? t : 0), + r.getSeconds() + (e === A ? t : 0), + r.getMilliseconds() + (e === L ? t : 0) ]; return new Date(...s); }, - start_of(n, t) { + start_of(r, t) { const e = { [k]: 6, [D]: 5, [$]: 4, - [E]: 3, - [Y]: 2, + [Y]: 3, + [E]: 2, [A]: 1, [L]: 0 }; function s(o) { - const r = e[t]; - return e[o] <= r; + const n = e[t]; + return e[o] <= n; } const i = [ - n.getFullYear(), - s(k) ? 0 : n.getMonth(), - s(D) ? 1 : n.getDate(), - s($) ? 0 : n.getHours(), - s(E) ? 0 : n.getMinutes(), - s(Y) ? 0 : n.getSeconds(), - s(A) ? 0 : n.getMilliseconds() + r.getFullYear(), + s(k) ? 0 : r.getMonth(), + s(D) ? 1 : r.getDate(), + s($) ? 0 : r.getHours(), + s(Y) ? 0 : r.getMinutes(), + s(E) ? 0 : r.getSeconds(), + s(A) ? 0 : r.getMilliseconds() ]; return new Date(...i); }, - clone(n) { - return new Date(...this.get_date_values(n)); + clone(r) { + return new Date(...this.get_date_values(r)); }, - get_date_values(n) { + get_date_values(r) { return [ - n.getFullYear(), - n.getMonth(), - n.getDate(), - n.getHours(), - n.getMinutes(), - n.getSeconds(), - n.getMilliseconds() + r.getFullYear(), + r.getMonth(), + r.getDate(), + r.getHours(), + r.getMinutes(), + r.getSeconds(), + r.getMilliseconds() ]; }, - get_days_in_month(n) { - const t = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], e = n.getMonth(); + get_days_in_month(r) { + const t = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], e = r.getMonth(); if (e !== 1) return t[e]; - const s = n.getFullYear(); + const s = r.getFullYear(); return s % 4 === 0 && s % 100 != 0 || s % 400 === 0 ? 29 : 28; } }; -function v(n, t, e) { - return n = n + "", t = t >> 0, e = String(typeof e < "u" ? e : " "), n.length > t ? String(n) : (t = t - n.length, t > e.length && (e += e.repeat(t / e.length)), e.slice(0, t) + String(n)); +function v(r, t, e) { + return r = r + "", t = t >> 0, e = String(typeof e < "u" ? e : " "), r.length > t ? String(r) : (t = t - r.length, t > e.length && (e += e.repeat(t / e.length)), e.slice(0, t) + String(r)); } -function _(n, t) { - return typeof n == "string" ? (t || document).querySelector(n) : n || null; +function u(r, t) { + return typeof r == "string" ? (t || document).querySelector(r) : r || null; } -function u(n, t) { - const e = document.createElementNS("http://www.w3.org/2000/svg", n); +function c(r, t) { + const e = document.createElementNS("http://www.w3.org/2000/svg", r); for (let s in t) s === "append_to" ? t.append_to.appendChild(e) : s === "innerHTML" ? e.innerHTML = t.innerHTML : s === "clipPath" ? e.setAttribute("clip-path", "url(#" + t[s] + ")") : e.setAttribute(s, t[s]); return e; } -function H(n, t, e, s) { - const i = W(n, t, e, s); - if (i === n) { +function H(r, t, e, s) { + const i = W(r, t, e, s); + if (i === r) { const o = document.createEvent("HTMLEvents"); o.initEvent("click", !0, !0), o.eventName = "click", i.dispatchEvent(o); } } -function W(n, t, e, s, i = "0.4s", o = "0.1s") { - const r = n.querySelector("animate"); - if (r) - return _.attr(r, { +function W(r, t, e, s, i = "0.4s", o = "0.1s") { + const n = r.querySelector("animate"); + if (n) + return u.attr(n, { attributeName: t, from: e, to: s, dur: i, begin: "click + " + o // artificial click - }), n; - const a = u("animate", { + }), r; + const a = c("animate", { attributeName: t, from: e, to: s, @@ -195,46 +195,46 @@ function W(n, t, e, s, i = "0.4s", o = "0.1s") { keyTimes: "0; 1", keySplines: X("ease-out") }); - return n.appendChild(a), n; + return r.appendChild(a), r; } -function X(n) { +function X(r) { return { ease: ".25 .1 .25 1", linear: "0 0 1 1", "ease-in": ".42 0 1 1", "ease-out": "0 0 .58 1", "ease-in-out": ".42 0 .58 1" - }[n]; + }[r]; } -_.on = (n, t, e, s) => { - s ? _.delegate(n, t, e, s) : (s = e, _.bind(n, t, s)); +u.on = (r, t, e, s) => { + s ? u.delegate(r, t, e, s) : (s = e, u.bind(r, t, s)); }; -_.off = (n, t, e) => { - n.removeEventListener(t, e); +u.off = (r, t, e) => { + r.removeEventListener(t, e); }; -_.bind = (n, t, e) => { +u.bind = (r, t, e) => { t.split(/\s+/).forEach(function(s) { - n.addEventListener(s, e); + r.addEventListener(s, e); }); }; -_.delegate = (n, t, e, s) => { - n.addEventListener(t, function(i) { +u.delegate = (r, t, e, s) => { + r.addEventListener(t, function(i) { const o = i.target.closest(e); o && (i.delegatedTarget = o, s.call(this, i, o)); }); }; -_.closest = (n, t) => t ? t.matches(n) ? t : _.closest(n, t.parentNode) : null; -_.attr = (n, t, e) => { +u.closest = (r, t) => t ? t.matches(r) ? t : u.closest(r, t.parentNode) : null; +u.attr = (r, t, e) => { if (!e && typeof t == "string") - return n.getAttribute(t); + return r.getAttribute(t); if (typeof t == "object") { for (let s in t) - _.attr(n, s, t[s]); + u.attr(r, s, t[s]); return; } - n.setAttribute(t, e); + r.setAttribute(t, e); }; -class O { +class C { constructor(t, e) { this.set_defaults(t, e), this.prepare(), this.draw(), this.bind(); } @@ -245,13 +245,13 @@ class O { this.prepare_values(), this.prepare_helpers(); } prepare_values() { - this.invalid = this.task.invalid, this.height = this.gantt.options.bar_height, this.image_size = this.height - 5, this.compute_x(), this.compute_y(), this.compute_duration(), this.corner_radius = this.gantt.options.bar_corner_radius, this.width = this.gantt.options.column_width * this.duration, this.progress_width = this.gantt.options.column_width * this.duration * (this.task.progress / 100) || 0, this.group = u("g", { + this.invalid = this.task.invalid, this.height = this.gantt.options.bar_height, this.image_size = this.height - 5, this.compute_x(), this.compute_y(), this.compute_duration(), this.corner_radius = this.gantt.options.bar_corner_radius, this.width = this.gantt.options.column_width * this.duration, this.progress_width = this.gantt.options.column_width * this.duration * (this.task.progress / 100) || 0, this.group = c("g", { class: "bar-wrapper" + (this.task.custom_class ? " " + this.task.custom_class : "") + (this.task.important ? " important" : ""), "data-id": this.task.id - }), this.bar_group = u("g", { + }), this.bar_group = c("g", { class: "bar-group", append_to: this.group - }), this.handle_group = u("g", { + }), this.handle_group = c("g", { class: "handle-group", append_to: this.group }); @@ -276,7 +276,7 @@ class O { this.draw_bar(), this.draw_progress_bar(), this.gantt.options.show_expected_progress && (this.prepare_expected_progress_values(), this.draw_expected_progress_bar()), this.draw_label(), this.draw_resize_handles(), this.task.thumbnail && this.draw_thumbnail(); } draw_bar() { - this.$bar = u("rect", { + this.$bar = c("rect", { x: this.x, y: this.y, width: this.width, @@ -288,7 +288,7 @@ class O { }), H(this.$bar, "width", 0, this.width), this.invalid && this.$bar.classList.add("bar-invalid"); } draw_expected_progress_bar() { - this.invalid || (this.$expected_bar_progress = u("rect", { + this.invalid || (this.$expected_bar_progress = c("rect", { x: this.x, y: this.y, width: this.expected_progress_width, @@ -307,7 +307,7 @@ class O { draw_progress_bar() { if (this.invalid) return; - this.$bar_progress = u("rect", { + this.$bar_progress = c("rect", { x: this.x, y: this.y, width: this.progress_width, @@ -323,7 +323,7 @@ class O { } draw_label() { let t = this.x + this.$bar.getWidth() / 2; - this.task.thumbnail && (t = this.x + this.image_size + 5), u("text", { + this.task.thumbnail && (t = this.x + this.image_size + 5), c("text", { x: t, y: this.y + this.height / 2, innerHTML: this.task.name, @@ -333,9 +333,9 @@ class O { } draw_thumbnail() { let t = 10, e = 2, s, i; - s = u("defs", { + s = c("defs", { append_to: this.bar_group - }), u("rect", { + }), c("rect", { id: "rect_" + this.task.id, x: this.x + t, y: this.y + e, @@ -344,13 +344,13 @@ class O { rx: "15", class: "img_mask", append_to: s - }), i = u("clipPath", { + }), i = c("clipPath", { id: "clip_" + this.task.id, append_to: s - }), u("use", { + }), c("use", { href: "#rect_" + this.task.id, append_to: i - }), u("image", { + }), c("image", { x: this.x + t, y: this.y + e, width: this.image_size, @@ -365,7 +365,7 @@ class O { if (this.invalid || this.gantt.options.readonly) return; const t = this.$bar, e = 8; - if (this.gantt.options.dates_readonly || (u("rect", { + if (this.gantt.options.dates_readonly || (c("rect", { x: t.getX() + t.getWidth() + e - 4, y: t.getY() + 1, width: e, @@ -374,7 +374,7 @@ class O { ry: this.corner_radius, class: "handle right", append_to: this.handle_group - }), u("rect", { + }), c("rect", { x: t.getX() - e - 4, y: t.getY() + 1, width: e, @@ -385,7 +385,7 @@ class O { append_to: this.handle_group })), !this.gantt.options.progress_readonly) { const s = this.$bar_progress; - this.$handle_progress = u("circle", { + this.$handle_progress = c("circle", { cx: s.getEndX(), cy: s.getY() + s.getHeight() / 2, r: 5, @@ -399,7 +399,7 @@ class O { } setup_click_event() { let t = this.task.id; - if (_.on(this.group, "mouseover", (e) => { + if (u.on(this.group, "mouseover", (e) => { this.gantt.trigger_event("hover", [ this.task, e.screenX, @@ -408,14 +408,14 @@ class O { ]); }), this.gantt.options.popup_on === "click") { let e = !1; - _.on(this.group, "click", (s) => { + u.on(this.group, "click", (s) => { console.log(e), e ? this.gantt.hide_popup() : (this.show_popup(s.offsetX || s.layerX), document.getElementById( `${t}-highlight` ).style.display = "block"), e = !e; }); } else { let e; - _.on( + u.on( this.group, "mouseenter", (s) => e = setTimeout(() => { @@ -423,14 +423,14 @@ class O { `${t}-highlight` ).style.display = "block"; }, 200) - ), _.on(this.group, "mouseleave", () => { + ), u.on(this.group, "mouseleave", () => { var s, i; clearTimeout(e), (i = (s = this.gantt.popup) == null ? void 0 : s.hide) == null || i.call(s), document.getElementById(`${t}-highlight`).style.display = "none"; }); } - _.on(this.group, "click", () => { + u.on(this.group, "click", () => { this.gantt.trigger_event("click", [this.task]); - }), _.on(this.group, "dblclick", (e) => { + }), u.on(this.group, "dblclick", (e) => { this.action_completed || (this.group.classList.remove("active"), this.gantt.popup && this.gantt.popup.parent.classList.remove("hidden"), this.gantt.trigger_event("double_click", [this.task])); }); } @@ -457,7 +457,7 @@ class O { update_bar_position({ x: t = null, width: e = null }) { const s = this.$bar; if (t) { - if (!this.task.dependencies.map((r) => this.gantt.get_bar(r).$bar.getX()).reduce((r, a) => t >= a, t)) { + if (!this.task.dependencies.map((n) => this.gantt.get_bar(n).$bar.getX()).reduce((n, a) => t >= a, t)) { e = null; return; } @@ -466,9 +466,9 @@ class O { e && (this.update_attr(s, "width", e), this.$date_highlight.style.width = e + "px"), this.update_label_position(), this.update_handle_position(), this.gantt.options.show_expected_progress && (this.date_changed(), this.compute_duration(), this.update_expected_progressbar_position()), this.update_progressbar_position(), this.update_arrow_position(); } update_label_position_on_horizontal_scroll({ x: t, sx: e }) { - const s = document.querySelector(".gantt-container"), i = this.group.querySelector(".bar-label"), o = this.group.querySelector(".bar-img") || "", r = this.bar_group.querySelector(".img_mask") || ""; - let a = this.$bar.getX() + this.$bar.getWidth(), p = i.getX() + t, d = o && o.getX() + t || 0, g = o && o.getBBox().width + 7 || 7, c = p + i.getBBox().width + 7, f = e + s.clientWidth / 2; - i.classList.contains("big") || (c < a && t > 0 && c < f || p - g > this.$bar.getX() && t < 0 && c > f) && (i.setAttribute("x", p), o && (o.setAttribute("x", d), r.setAttribute("x", d))); + const s = document.querySelector(".gantt-container"), i = this.group.querySelector(".bar-label"), o = this.group.querySelector(".bar-img") || "", n = this.bar_group.querySelector(".img_mask") || ""; + let a = this.$bar.getX() + this.$bar.getWidth(), p = i.getX() + t, d = o && o.getX() + t || 0, l = o && o.getBBox().width + 7 || 7, g = p + i.getBBox().width + 7, f = e + s.clientWidth / 2; + i.classList.contains("big") || (g < a && t > 0 && g < f || p - l > this.$bar.getX() && t < 0 && g > f) && (i.setAttribute("x", p), o && (o.setAttribute("x", d), n.setAttribute("x", d))); } date_changed() { let t = !1; @@ -499,12 +499,12 @@ class O { i, "minute" )); - const o = t.getWidth() / this.gantt.options.column_width, r = h.add( + const o = t.getWidth() / this.gantt.options.column_width, n = h.add( s, o * this.gantt.options.step, "hour" ); - return { new_start_date: s, new_end_date: r }; + return { new_start_date: s, new_end_date: n }; } compute_progress() { const t = this.$bar_progress.getWidth() / this.$bar.getWidth() * 100; @@ -515,15 +515,15 @@ class O { } compute_x() { const { step: t, column_width: e } = this.gantt.options, s = this.task._start, i = this.gantt.gantt_start; - let r = h.diff(s, i, "hour") / t * e; + let n = h.diff(s, i, "hour") / t * e; if (this.gantt.view_is("Month")) { const a = h.diff(s, i, "month") * 30, p = Math.min( 29, h.format(s, "DD") ); - r = (a + p) * e / 30; + n = (a + p) * e / 30; } - this.x = r; + this.x = n; } compute_y() { this.y = this.gantt.options.header_height + this.gantt.options.padding + this.task._index * (this.height + this.gantt.options.padding); @@ -552,17 +552,17 @@ class O { } update_label_position() { const t = this.bar_group.querySelector(".img_mask") || "", e = this.$bar, s = this.group.querySelector(".bar-label"), i = this.group.querySelector(".bar-img"); - let o = 5, r = this.image_size + 10; + let o = 5, n = this.image_size + 10; const a = s.getBBox().width, p = e.getWidth(); a > p ? (s.classList.add("big"), i ? (i.setAttribute("x", e.getX() + e.getWidth() + o), t.setAttribute( "x", e.getX() + e.getWidth() + o ), s.setAttribute( "x", - e.getX() + e.getWidth() + r + e.getX() + e.getWidth() + n )) : s.setAttribute("x", e.getX() + e.getWidth() + o)) : (s.classList.remove("big"), i ? (i.setAttribute("x", e.getX() + o), t.setAttribute("x", e.getX() + o), s.setAttribute( "x", - e.getX() + p / 2 + r + e.getX() + p / 2 + n )) : s.setAttribute( "x", e.getX() + p / 2 - a / 2 @@ -582,7 +582,7 @@ class O { t.update(); } } -class C { +class O { constructor(t, e, s) { this.gantt = t, this.from_task = e, this.to_task = s, this.calculate_path(), this.draw(); } @@ -591,19 +591,19 @@ class C { const e = () => this.to_task.$bar.getX() < t + this.gantt.options.padding && t > this.from_task.$bar.getX() + this.gantt.options.padding; for (; e(); ) t -= 10; - const s = this.gantt.options.header_height + this.gantt.options.bar_height + (this.gantt.options.padding + this.gantt.options.bar_height) * this.from_task.task._index + this.gantt.options.padding, i = this.to_task.$bar.getX() - this.gantt.options.padding / 2 - 7, o = this.gantt.options.header_height + this.gantt.options.bar_height / 2 + (this.gantt.options.padding + this.gantt.options.bar_height) * this.to_task.task._index + this.gantt.options.padding, r = this.from_task.task._index > this.to_task.task._index, a = this.gantt.options.arrow_curve, p = r ? 1 : 0, d = r ? -a : a, g = r ? o + this.gantt.options.arrow_curve : o - this.gantt.options.arrow_curve; + const s = this.gantt.options.header_height + this.gantt.options.bar_height + (this.gantt.options.padding + this.gantt.options.bar_height) * this.from_task.task._index + this.gantt.options.padding, i = this.to_task.$bar.getX() - this.gantt.options.padding / 2 - 7, o = this.gantt.options.header_height + this.gantt.options.bar_height / 2 + (this.gantt.options.padding + this.gantt.options.bar_height) * this.to_task.task._index + this.gantt.options.padding, n = this.from_task.task._index > this.to_task.task._index, a = this.gantt.options.arrow_curve, p = n ? 1 : 0, d = n ? -a : a, l = n ? o + this.gantt.options.arrow_curve : o - this.gantt.options.arrow_curve; if (this.path = ` M ${t} ${s} - V ${g} + V ${l} a ${a} ${a} 0 0 ${p} ${a} ${d} L ${i} ${o} m -5 -5 l 5 5 l -5 5`, this.to_task.$bar.getX() < this.from_task.$bar.getX() + this.gantt.options.padding) { - const c = this.gantt.options.padding / 2 - a, f = this.to_task.$bar.getY() + this.to_task.$bar.getHeight() / 2 - d, w = this.to_task.$bar.getX() - this.gantt.options.padding; + const g = this.gantt.options.padding / 2 - a, f = this.to_task.$bar.getY() + this.to_task.$bar.getHeight() / 2 - d, w = this.to_task.$bar.getX() - this.gantt.options.padding; this.path = ` M ${t} ${s} - v ${c} + v ${g} a ${a} ${a} 0 0 1 -${a} ${a} H ${w} a ${a} ${a} 0 0 ${p} -${a} ${d} @@ -616,7 +616,7 @@ class C { } } draw() { - this.element = u("path", { + this.element = c("path", { d: this.path, "data-from": this.from_task.task.id, "data-to": this.to_task.task.id @@ -654,7 +654,7 @@ class N { this.parent.style.opacity = 0, this.parent.style.left = 0; } } -const l = { +const _ = { HOUR: "Hour", QUARTER_DAY: "Quarter Day", HALF_DAY: "Half Day", @@ -670,10 +670,10 @@ const l = { WEEK: ["1m", "1m"], MONTH: ["1m", "1m"], YEAR: ["2y", "2y"] -}, I = { +}, F = { header_height: 65, column_width: 30, - view_modes: [...Object.values(l)], + view_modes: [...Object.values(_)], bar_height: 30, bar_corner_radius: 3, arrow_curve: 5, @@ -694,7 +694,7 @@ const l = { today_button: !0, view_mode_select: !1 }; -class B { +class I { constructor(t, e, s) { this.setup_wrapper(t), this.setup_options(s), this.setup_tasks(e), this.change_view_mode(), this.bind_events(); } @@ -708,15 +708,17 @@ class B { throw new TypeError( "Frappe Gantt only supports usage of a string CSS selector, HTML DOM element or SVG DOM element for the 'element' parameter" ); - e ? (this.$svg = e, this.$svg.classList.add("gantt")) : this.$svg = u("svg", { + e ? (this.$svg = e, this.$svg.classList.add("gantt")) : this.$svg = c("svg", { append_to: s, class: "gantt" }), this.$container = document.createElement("div"), this.$container.classList.add("gantt-container"), this.$svg.parentElement.appendChild(this.$container), this.$container.appendChild(this.$svg), this.$popup_wrapper = document.createElement("div"), this.$popup_wrapper.classList.add("popup-wrapper"), this.$container.appendChild(this.$popup_wrapper); } setup_options(t) { - this.options = { ...I, ...t }, t.view_mode_padding || (t.view_mode_padding = {}); - for (let [e, s] of Object.entries(t.view_mode_padding)) - typeof s == "string" && (t.view_mode_padding[e] = [s, s]); + this.options = { ...F, ...t }; + const e = this.options.custom_view_modes ? this.options.custom_view_modes.find((s) => s.name === this.options.view_mode) : null; + e && (this.options = { ...this.options, custom_mode: e }), this.options.view_mode_padding || (this.options.view_mode_padding = {}); + for (let [s, i] of Object.entries(this.options.view_mode_padding)) + typeof i == "string" && (this.options.view_mode_padding[s] = [i, i]); this.options.view_mode_padding = { ...R, ...t.view_mode_padding @@ -732,14 +734,14 @@ class B { "start of task can't be after end of task: in task #, " + (s + 1) ); if (h.diff(e._end, e._start, "year") > 10 && (e.end = null), e._index = s, !e.start && !e.end) { - const r = h.today(); - e._start = r, e._end = h.add(r, 2, "day"); + const n = h.today(); + e._start = n, e._end = h.add(n, 2, "day"); } - if (!e.start && e.end && (e._start = h.add(e._end, -2, "day")), e.start && !e.end && (e._end = h.add(e._start, 2, "day")), h.get_date_values(e._end).slice(3).every((r) => r === 0) && (e._end = h.add(e._end, 24, "hour")), (!e.start || !e.end) && (e.invalid = !0), typeof e.dependencies == "string" || !e.dependencies) { - let r = []; - e.dependencies && (r = e.dependencies.split(",").map((a) => a.trim().replaceAll(" ", "_")).filter((a) => a)), e.dependencies = r; + if (!e.start && e.end && (e._start = h.add(e._end, -2, "day")), e.start && !e.end && (e._end = h.add(e._start, 2, "day")), h.get_date_values(e._end).slice(3).every((n) => n === 0) && (e._end = h.add(e._end, 24, "hour")), (!e.start || !e.end) && (e.invalid = !0), typeof e.dependencies == "string" || !e.dependencies) { + let n = []; + e.dependencies && (n = e.dependencies.split(",").map((a) => a.trim().replaceAll(" ", "_")).filter((a) => a)), e.dependencies = n; } - return e.id ? typeof e.id == "string" ? e.id = e.id.replaceAll(" ", "_") : e.id = `${e.id}` : e.id = F(e), e; + return e.id ? typeof e.id == "string" ? e.id = e.id.replaceAll(" ", "_") : e.id = `${e.id}` : e.id = B(e), e; }), this.setup_dependencies(); } setup_dependencies() { @@ -755,7 +757,9 @@ class B { this.update_view_scale(t), this.setup_dates(), this.render(), this.trigger_event("view_change", [t]); } update_view_scale(t) { - this.options.view_mode = t, t === l.HOUR ? (this.options.step = 24 / 24, this.options.column_width = 38) : t === l.DAY ? (this.options.step = 24, this.options.column_width = 38) : t === l.HALF_DAY ? (this.options.step = 24 / 2, this.options.column_width = 38) : t === l.QUARTER_DAY ? (this.options.step = 24 / 4, this.options.column_width = 38) : t === l.WEEK ? (this.options.step = 24 * 7, this.options.column_width = 140) : t === l.MONTH ? (this.options.step = 24 * 30, this.options.column_width = 120) : t === l.YEAR && (this.options.step = 24 * 365, this.options.column_width = 120); + this.options.view_mode = t; + const e = this.options.custom_mode; + e && (e.unit === "hour" ? (this.options.step = e.step, this.options.column_width = 38) : e.unit === "day" ? (this.options.step = e.step * 24, this.options.column_width = 38) : e.unit === "month" ? (this.options.step = e.step * 24 * 30, this.options.column_width = 120) : (this.options.step = 24, this.options.column_width = 38)), t === _.HOUR ? (this.options.step = 24 / 24, this.options.column_width = 38) : t === _.DAY ? (this.options.step = 24, this.options.column_width = 38) : t === _.HALF_DAY ? (this.options.step = 24 / 2, this.options.column_width = 38) : t === _.QUARTER_DAY ? (this.options.step = 24 / 4, this.options.column_width = 38) : t === _.WEEK ? (this.options.step = 24 * 7, this.options.column_width = 140) : t === _.MONTH ? (this.options.step = 24 * 30, this.options.column_width = 120) : t === _.YEAR && (this.options.step = 24 * 365, this.options.column_width = 120); } setup_dates() { this.setup_gantt_dates(), this.setup_date_values(); @@ -766,18 +770,24 @@ class B { (!this.gantt_start || a._start < this.gantt_start) && (this.gantt_start = a._start), (!this.gantt_end || a._end > this.gantt_end) && (this.gantt_end = a._end); let t, e; this.gantt_start ? t = h.start_of(this.gantt_start, "day") : t = /* @__PURE__ */ new Date(), this.gantt_end ? e = h.start_of(this.gantt_end, "day") : e = /* @__PURE__ */ new Date(); - let s; - for (let [a, p] of Object.entries(l)) - p === this.options.view_mode && (s = a); - const [i, o] = this.options.view_mode_padding[s].map(h.parse_duration); + const s = this.options.custom_mode; + let [i, o] = [{ duration: 1, scale: "day" }, { duration: 1, scale: "day" }]; + if (s) + [i, o] = [s.padding, s.padding].map(h.parse_duration); + else { + let a; + for (let [p, d] of Object.entries(_)) + d === this.options.view_mode && (a = p); + [i, o] = this.options.view_mode_padding[a].map(h.parse_duration); + } t = h.add( t, -i.duration, i.scale ); - let r; - this.view_is(l.YEAR) ? r = "YYYY" : this.view_is(l.MONTH) ? r = "YYYY-MM" : this.view_is(l.DAY) ? r = "YYYY-MM-DD" : r = "YYYY-MM-DD HH", this.gantt_start = h.parse( - h.format(t, r) + let n; + this.view_is(_.YEAR) ? n = "YYYY" : this.view_is(_.MONTH) ? n = "YYYY-MM" : this.view_is(_.DAY) ? n = "YYYY-MM-DD" : n = "YYYY-MM-DD HH", this.gantt_start = h.parse( + h.format(t, n) ), this.gantt_start.setHours(0, 0, 0, 0), this.gantt_end = h.add( e, o.duration, @@ -787,12 +797,18 @@ class B { setup_date_values() { this.dates = []; let t = null; - for (; t === null || t < this.gantt_end; ) - t ? this.view_is(l.YEAR) ? t = h.add(t, 1, "year") : this.view_is(l.MONTH) ? t = h.add(t, 1, "month") : t = h.add( - t, - this.options.step, - "hour" - ) : t = h.clone(this.gantt_start), this.dates.push(t); + for (; t === null || t < this.gantt_end; ) { + if (this.options.custom_mode) { + const e = this.options.custom_mode.step || 1, s = this.options.custom_mode.unit || "day"; + t ? t = h.add(t, e, s) : t = h.clone(this.gantt_start); + } else + t ? this.view_is(_.YEAR) ? t = h.add(t, 1, "year") : this.view_is(_.MONTH) ? t = h.add(t, 1, "month") : t = h.add( + t, + this.options.step, + "hour" + ) : t = h.clone(this.gantt_start); + this.dates.push(t); + } } bind_events() { this.options.readonly || (this.bind_grid_click(), this.bind_bar_events()); @@ -804,7 +820,7 @@ class B { this.layers = {}; const t = ["grid", "arrow", "progress", "bar", "details"]; for (let e of t) - this.layers[e] = u("g", { + this.layers[e] = c("g", { class: e, append_to: this.$svg }); @@ -817,23 +833,23 @@ class B { } make_grid_background() { const t = this.dates.length * this.options.column_width, e = this.options.header_height + this.options.padding + (this.options.bar_height + this.options.padding) * this.tasks.length; - u("rect", { + c("rect", { x: 0, y: 0, width: t, height: e, class: "grid-background", append_to: this.$svg - }), _.attr(this.$svg, { + }), u.attr(this.$svg, { height: e + this.options.padding + 100, width: "100%" }); } make_grid_rows() { - const t = u("g", { append_to: this.layers.grid }), e = this.dates.length * this.options.column_width, s = this.options.bar_height + this.options.padding; + const t = c("g", { append_to: this.layers.grid }), e = this.dates.length * this.options.column_width, s = this.options.bar_height + this.options.padding; let i = this.options.header_height + this.options.padding / 2; for (let o of this.tasks) - u("rect", { + c("rect", { x: 0, y: i, width: e, @@ -857,9 +873,9 @@ class B { e.classList.add("viewmode-select"); const s = document.createElement("option"); s.selected = !0, s.disabled = !0, s.textContent = "Mode", e.appendChild(s); - for (const i in l) { + for (const i in _) { const o = document.createElement("option"); - o.value = l[i], o.textContent = l[i], e.appendChild(o); + o.value = _[i], o.textContent = _[i], e.appendChild(o); } e.addEventListener( "change", @@ -892,17 +908,17 @@ class B { make_grid_ticks() { if (!["both", "vertical", "horizontal"].includes(this.options.lines)) return; - let t = 0, e = this.options.header_height + this.options.padding / 2, s = (this.options.bar_height + this.options.padding) * this.tasks.length, i = u("g", { + let t = 0, e = this.options.header_height + this.options.padding / 2, s = (this.options.bar_height + this.options.padding) * this.tasks.length, i = c("g", { class: "lines_layer", append_to: this.layers.grid }), o = this.options.header_height + this.options.padding / 2; - const r = this.dates.length * this.options.column_width, a = this.options.bar_height + this.options.padding; + const n = this.dates.length * this.options.column_width, a = this.options.bar_height + this.options.padding; if (this.options.lines !== "vertical") for (let p of this.tasks) - u("line", { + c("line", { x1: 0, y1: o + a, - x2: r, + x2: n, y2: o + a, class: "row-line", append_to: i @@ -910,11 +926,11 @@ class B { if (this.options.lines !== "horizontal") for (let p of this.dates) { let d = "tick"; - this.view_is(l.DAY) && p.getDate() === 1 && (d += " thick"), this.view_is(l.WEEK) && p.getDate() >= 1 && p.getDate() < 8 && (d += " thick"), this.view_is(l.MONTH) && p.getMonth() % 3 === 0 && (d += " thick"), u("path", { + this.view_is(_.DAY) && p.getDate() === 1 && (d += " thick"), this.view_is(_.WEEK) && p.getDate() >= 1 && p.getDate() < 8 && (d += " thick"), this.view_is(_.MONTH) && p.getMonth() % 3 === 0 && (d += " thick"), c("path", { d: `M ${t} ${e} v ${s}`, class: d, append_to: this.layers.grid - }), this.view_is(l.MONTH) ? t += h.get_days_in_month(p) * this.options.column_width / 30 : t += this.options.column_width; + }), this.view_is(_.MONTH) ? t += h.get_days_in_month(p) * this.options.column_width / 30 : t += this.options.column_width; } } highlightWeekends() { @@ -922,7 +938,7 @@ class B { for (let t = new Date(this.gantt_start); t <= this.gantt_end; t.setDate(t.getDate() + 1)) if (t.getDay() === 0 || t.getDay() === 6) { const e = h.diff(t, this.gantt_start, "hour") / this.options.step * this.options.column_width, s = (this.options.bar_height + this.options.padding) * this.tasks.length; - u("rect", { + c("rect", { x: e, y: this.options.header_height + this.options.padding / 2, width: (this.view_is("Day") ? 1 : 2) * this.options.column_width, @@ -936,7 +952,7 @@ class B { //compute the horizontal x distance computeGridHighlightDimensions(t) { let e = this.options.column_width / 2; - if (this.view_is(l.DAY)) { + if (this.view_is(_.DAY)) { let s = h.today(); return { x: e + h.diff(s, this.gantt_start, "hour") / this.options.step * this.options.column_width, @@ -944,30 +960,30 @@ class B { }; } for (let s of this.dates) { - const i = /* @__PURE__ */ new Date(), o = new Date(s), r = new Date(s); + const i = /* @__PURE__ */ new Date(), o = new Date(s), n = new Date(s); switch (t) { - case l.WEEK: - r.setDate(s.getDate() + 7); + case _.WEEK: + n.setDate(s.getDate() + 7); break; - case l.MONTH: - r.setMonth(s.getMonth() + 1); + case _.MONTH: + n.setMonth(s.getMonth() + 1); break; - case l.YEAR: - r.setFullYear(s.getFullYear() + 1); + case _.YEAR: + n.setFullYear(s.getFullYear() + 1); break; } - if (i >= o && i <= r) + if (i >= o && i <= n) return { x: e, date: o }; e += this.options.column_width; } return { x: e }; } make_grid_highlights() { - if (this.options.highlight_weekend && this.highlightWeekends(), this.view_is(l.DAY) || this.view_is(l.WEEK) || this.view_is(l.MONTH) || this.view_is(l.YEAR)) { + if (this.options.highlight_weekend && this.highlightWeekends(), this.view_is(_.DAY) || this.view_is(_.WEEK) || this.view_is(_.MONTH) || this.view_is(_.YEAR)) { const { x: t, date: e } = this.computeGridHighlightDimensions( this.options.view_mode ); - if (!e || !this.dates.find((r) => r.getTime() == e.getTime())) + if (!e || !this.dates.find((n) => n.getTime() == e.getTime())) return; const s = this.options.header_height + this.options.padding / 2, i = (this.options.bar_height + this.options.padding) * this.tasks.length; this.$current_highlight = this.create_el({ @@ -983,9 +999,9 @@ class B { o && (o.classList.add("current-date-highlight"), o.style.top = +o.style.top.slice(0, -2) - 4 + "px", o.style.left = +o.style.left.slice(0, -2) - 8 + "px"); } } - create_el({ left: t, top: e, width: s, height: i, id: o, classes: r, append_to: a }) { + create_el({ left: t, top: e, width: s, height: i, id: o, classes: n, append_to: a }) { let p = document.createElement("div"); - return p.classList.add(r), p.style.top = e + "px", p.style.left = t + "px", o && (p.id = o), s && (p.style.width = i + "px"), i && (p.style.height = i + "px"), a.appendChild(p), p; + return p.classList.add(n), p.style.top = e + "px", p.style.left = t + "px", o && (p.id = o), s && (p.style.width = i + "px"), i && (p.style.height = i + "px"), a.appendChild(p), p; } make_dates() { this.upper_texts_x = {}, this.get_dates_to_draw().forEach((t, e) => { @@ -1011,61 +1027,67 @@ class B { }); } get_date_info(t, e) { - let s = e ? e.date : h.add(t, 1, "day"); - const i = { - Hour_lower: h.format(t, "HH", this.options.language), - "Quarter Day_lower": h.format( - t, - "HH", - this.options.language - ), - "Half Day_lower": h.format( - t, - "HH", - this.options.language - ), - Day_lower: t.getDate() !== s.getDate() ? h.format(t, "D", this.options.language) : "", - Week_lower: t.getMonth() !== s.getMonth() ? h.format(t, "D MMM", this.options.language) : h.format(t, "D", this.options.language), - Month_lower: h.format(t, "MMMM", this.options.language), - Year_lower: h.format(t, "YYYY", this.options.language), - Hour_upper: t.getDate() !== s.getDate() ? h.format(t, "D MMMM", this.options.language) : "", - "Quarter Day_upper": t.getDate() !== s.getDate() ? h.format(t, "D MMM", this.options.language) : "", - "Half Day_upper": t.getDate() !== s.getDate() ? t.getMonth() !== s.getMonth() ? h.format( - t, - "D MMM", - this.options.language - ) : h.format(t, "D", this.options.language) : "", - Day_upper: t.getMonth() !== s.getMonth() || !e ? h.format(t, "MMMM", this.options.language) : "", - Week_upper: t.getMonth() !== s.getMonth() ? h.format(t, "MMMM", this.options.language) : "", - Month_upper: t.getFullYear() !== s.getFullYear() ? h.format(t, "YYYY", this.options.language) : "", - Year_upper: t.getFullYear() !== s.getFullYear() ? h.format(t, "YYYY", this.options.language) : "" - }; - let o = this.view_is(l.MONTH) ? h.get_days_in_month(t) * this.options.column_width / 30 : this.options.column_width; - const r = { + let s = e ? e.date : h.add(t, 1, "day"), i = {}; + const o = this.options.custom_mode; + if (o) { + let d, l; + const g = o ? o.unit.toLowerCase() : "day"; + g === "hour" ? (d = h.format(t, "HH", this.options.language), l = t.getDate() !== s.getDate() ? h.format(t, "D MMMM", this.options.language) : "") : g === "day" ? (d = t.getDate() !== s.getDate() ? h.format(t, "D", this.options.language) : "", l = t.getMonth() !== s.getMonth() || !e ? h.format(t, "MMMM", this.options.language) : "") : g === "month" ? (d = h.format(t, "MMMM", this.options.language), l = t.getFullYear() !== s.getFullYear() ? h.format(t, "YYYY", this.options.language) : "") : (d = h.format(t, "YYYY", this.options.language), l = ""), i[`${o.name}_upper`] = l, i[`${o.name}_lower`] = d; + } else + i = { + Hour_lower: h.format(t, "HH", this.options.language), + "Quarter Day_lower": h.format( + t, + "HH", + this.options.language + ), + "Half Day_lower": h.format( + t, + "HH", + this.options.language + ), + Day_lower: t.getDate() !== s.getDate() ? h.format(t, "D", this.options.language) : "", + Week_lower: t.getMonth() !== s.getMonth() ? h.format(t, "D MMM", this.options.language) : h.format(t, "D", this.options.language), + Month_lower: h.format(t, "MMMM", this.options.language), + Year_lower: h.format(t, "YYYY", this.options.language), + Hour_upper: t.getDate() !== s.getDate() ? h.format(t, "D MMMM", this.options.language) : "", + "Quarter Day_upper": t.getDate() !== s.getDate() ? h.format(t, "D MMM", this.options.language) : "", + "Half Day_upper": t.getDate() !== s.getDate() ? t.getMonth() !== s.getMonth() ? h.format( + t, + "D MMM", + this.options.language + ) : h.format(t, "D", this.options.language) : "", + Day_upper: t.getMonth() !== s.getMonth() || !e ? h.format(t, "MMMM", this.options.language) : "", + Week_upper: t.getMonth() !== s.getMonth() ? h.format(t, "MMMM", this.options.language) : "", + Month_upper: t.getFullYear() !== s.getFullYear() ? h.format(t, "YYYY", this.options.language) : "", + Year_upper: t.getFullYear() !== s.getFullYear() ? h.format(t, "YYYY", this.options.language) : "" + }; + let n = o && o.lower_text && o.lower_text.column_width ? o.lower_text.column_width * (o.step ?? 1) : this.view_is(_.MONTH) ? h.get_days_in_month(t) * this.options.column_width / 30 : this.options.column_width; + const a = { x: e ? e.base_pos_x + e.column_width : 0, lower_y: this.options.header_height - 20, upper_y: this.options.header_height - 50 - }, a = { - Hour_lower: o / 2, - Hour_upper: o * 12, - "Quarter Day_lower": o / 2, - "Quarter Day_upper": o * 2, - "Half Day_lower": o / 2, - "Half Day_upper": o, - Day_lower: o / 2, - Day_upper: o / 2, - Week_lower: o / 2, - Week_upper: o * 4 / 2, - Month_lower: o / 2, - Month_upper: o / 2, - Year_lower: o / 2, - Year_upper: o * 30 / 2 + }, p = { + Hour_lower: n / 2, + Hour_upper: n * 12, + "Quarter Day_lower": n / 2, + "Quarter Day_upper": n * 2, + "Half Day_lower": n / 2, + "Half Day_upper": n, + Day_lower: n / 2, + Day_upper: n / 2, + Week_lower: n / 2, + Week_upper: n * 4 / 2, + Month_lower: n / 2, + Month_upper: n / 2, + Year_lower: n / 2, + Year_upper: n * 30 / 2 }; - return { + return o && (p[`${o.name}_upper`] = n / 2, p[`${o.name}_lower`] = n / (o.unit.toLowerCase() === "day" ? 1 : 2)), { date: t, formatted_date: h.format(t).replaceAll(" ", "_"), - column_width: o, - base_pos_x: r.x, + column_width: n, + base_pos_x: a.x, upper_text: this.options.lower_text ? this.options.upper_text( t, this.options.view_mode, @@ -1076,15 +1098,15 @@ class B { this.options.view_mode, i[`${this.options.view_mode}_lower`] ) : i[`${this.options.view_mode}_lower`], - upper_x: r.x + a[`${this.options.view_mode}_upper`], - upper_y: r.upper_y, - lower_x: r.x + a[`${this.options.view_mode}_lower`], - lower_y: r.lower_y + upper_x: a.x + p[`${this.options.view_mode}_upper`], + upper_y: a.upper_y, + lower_x: a.x + p[`${this.options.view_mode}_lower`], + lower_y: a.lower_y }; } make_bars() { this.bars = this.tasks.map((t) => { - const e = new O(this, t); + const e = new C(this, t); return this.layers.bar.appendChild(e.group), e; }); } @@ -1096,7 +1118,7 @@ class B { const i = this.get_task(s); if (!i) return; - const o = new C( + const o = new O( this, this.bars[i._index], // from_task @@ -1133,29 +1155,29 @@ class B { this.set_scroll_position(/* @__PURE__ */ new Date()); } bind_grid_click() { - _.on(this.$svg, "click", ".grid-row, .grid-header", () => { + u.on(this.$svg, "click", ".grid-row, .grid-header", () => { this.unselect_all(), this.hide_popup(); }); } bind_bar_events() { - let t = !1, e = 0, s = 0, i = !1, o = !1, r = null, a = []; + let t = !1, e = 0, s = 0, i = !1, o = !1, n = null, a = []; this.bar_being_dragged = null; function p() { return t || i || o; } this.$svg.onclick = (d) => { d.target.classList.contains("grid-row") && this.unselect_all(); - }, _.on(this.$svg, "mousedown", ".bar-wrapper, .handle", (d, g) => { - const c = _.closest(".bar-wrapper", g); - a.forEach((w) => w.group.classList.remove("active")), g.classList.contains("left") ? i = !0 : g.classList.contains("right") ? o = !0 : g.classList.contains("bar-wrapper") && (t = !0), c.classList.add("active"), this.popup && this.popup.parent.classList.add("hidden"), this.popup && this.popup.parent.classList.add("hidden"), e = d.offsetX || d.layerX, d.offsetY || d.layerY, r = c.getAttribute("data-id"), a = [ - r, - ...this.get_all_dependent_tasks(r) - ].map((w) => this.get_bar(w)), this.bar_being_dragged = r, a.forEach((w) => { + }, u.on(this.$svg, "mousedown", ".bar-wrapper, .handle", (d, l) => { + const g = u.closest(".bar-wrapper", l); + a.forEach((w) => w.group.classList.remove("active")), l.classList.contains("left") ? i = !0 : l.classList.contains("right") ? o = !0 : l.classList.contains("bar-wrapper") && (t = !0), g.classList.add("active"), this.popup && this.popup.parent.classList.add("hidden"), this.popup && this.popup.parent.classList.add("hidden"), e = d.offsetX || d.layerX, d.offsetY || d.layerY, n = g.getAttribute("data-id"), a = [ + n, + ...this.get_all_dependent_tasks(n) + ].map((w) => this.get_bar(w)), this.bar_being_dragged = n, a.forEach((w) => { const b = w.$bar; b.ox = b.getX(), b.oy = b.getY(), b.owidth = b.getWidth(), b.finaldx = 0; }); - }), _.on(this.$container, "scroll", (d) => { - let g = document.querySelectorAll(".bar-wrapper"), c = []; + }), u.on(this.$container, "scroll", (d) => { + let l = document.querySelectorAll(".bar-wrapper"), g = []; const f = []; let w; s && (w = d.currentTarget.scrollLeft - s); @@ -1177,65 +1199,65 @@ class B { let M = this.$svg.getBoundingClientRect(); x.style.left = M.x + this.$container.scrollLeft + 10 + "px", x.style.top = M.y + this.options.header_height - 50 + "px"; } - Array.prototype.forEach.call(g, function(m, M) { + Array.prototype.forEach.call(l, function(m, M) { f.push(m.getAttribute("data-id")); - }), w && (c = f.map((m) => this.get_bar(m)), this.options.auto_move_label && c.forEach((m) => { + }), w && (g = f.map((m) => this.get_bar(m)), this.options.auto_move_label && g.forEach((m) => { m.update_label_position_on_horizontal_scroll({ x: w, sx: d.currentTarget.scrollLeft }); })), s = d.currentTarget.scrollLeft; - }), _.on(this.$svg, "mousemove", (d) => { + }), u.on(this.$svg, "mousemove", (d) => { if (!p()) return; - const g = (d.offsetX || d.layerX) - e; - a.forEach((c) => { - const f = c.$bar; - f.finaldx = this.get_snap_position(g), this.hide_popup(), i ? r === c.task.id ? c.update_bar_position({ + const l = (d.offsetX || d.layerX) - e; + a.forEach((g) => { + const f = g.$bar; + f.finaldx = this.get_snap_position(l), this.hide_popup(), i ? n === g.task.id ? g.update_bar_position({ x: f.ox + f.finaldx, width: f.owidth - f.finaldx - }) : c.update_bar_position({ + }) : g.update_bar_position({ x: f.ox + f.finaldx - }) : o ? r === c.task.id && c.update_bar_position({ + }) : o ? n === g.task.id && g.update_bar_position({ width: f.owidth + f.finaldx - }) : t && !this.options.readonly && !this.options.dates_readonly && c.update_bar_position({ x: f.ox + f.finaldx }); + }) : t && !this.options.readonly && !this.options.dates_readonly && g.update_bar_position({ x: f.ox + f.finaldx }); }); }), document.addEventListener("mouseup", (d) => { t = !1, i = !1, o = !1; - }), _.on(this.$svg, "mouseup", (d) => { - this.bar_being_dragged = null, a.forEach((g) => { - g.$bar.finaldx && (g.date_changed(), g.set_action_completed()); + }), u.on(this.$svg, "mouseup", (d) => { + this.bar_being_dragged = null, a.forEach((l) => { + l.$bar.finaldx && (l.date_changed(), l.set_action_completed()); }); }), this.bind_bar_progress(); } bind_bar_progress() { let t = 0, e = null, s = null, i = null, o = null; - _.on(this.$svg, "mousedown", ".handle.progress", (r, a) => { - e = !0, t = r.offsetX || r.layerX, r.offsetY || r.layerY; - const d = _.closest(".bar-wrapper", a).getAttribute("data-id"); + u.on(this.$svg, "mousedown", ".handle.progress", (n, a) => { + e = !0, t = n.offsetX || n.layerX, n.offsetY || n.layerY; + const d = u.closest(".bar-wrapper", a).getAttribute("data-id"); s = this.get_bar(d), i = s.$bar_progress, o = s.$bar, i.finaldx = 0, i.owidth = i.getWidth(), i.min_dx = -i.getWidth(), i.max_dx = o.getWidth() - i.getWidth(); - }), _.on(this.$svg, "mousemove", (r) => { + }), u.on(this.$svg, "mousemove", (n) => { if (!e) return; - let a = (r.offsetX || r.layerX) - t; + let a = (n.offsetX || n.layerX) - t; a > i.max_dx && (a = i.max_dx), a < i.min_dx && (a = i.min_dx); const p = s.$handle_progress; - _.attr(i, "width", i.owidth + a), _.attr(p, "cx", i.getEndX()), i.finaldx = a; - }), _.on(this.$svg, "mouseup", () => { + u.attr(i, "width", i.owidth + a), u.attr(p, "cx", i.getEndX()), i.finaldx = a; + }), u.on(this.$svg, "mouseup", () => { e = !1, i && i.finaldx && (i.finaldx = 0, s.progress_changed(), s.set_action_completed(), s = null, i = null, o = null); }); } get_all_dependent_tasks(t) { let e = [], s = [t]; for (; s.length; ) { - const i = s.reduce((o, r) => (o = o.concat(this.dependency_map[r]), o), []); + const i = s.reduce((o, n) => (o = o.concat(this.dependency_map[n]), o), []); e = e.concat(i), s = i.filter((o) => !s.includes(o)); } return e.filter(Boolean); } get_snap_position(t) { let e = t, s, i; - return this.view_is(l.WEEK) ? (s = t % (this.options.column_width / 7), i = e - s + (s < this.options.column_width / 14 ? 0 : this.options.column_width / 7)) : this.view_is(l.MONTH) ? (s = t % (this.options.column_width / 30), i = e - s + (s < this.options.column_width / 60 ? 0 : this.options.column_width / 30)) : (s = t % this.options.column_width, i = e - s + (s < this.options.column_width / 2 ? 0 : this.options.column_width)), i; + return this.view_is(_.WEEK) ? (s = t % (this.options.column_width / 7), i = e - s + (s < this.options.column_width / 14 ? 0 : this.options.column_width / 7)) : this.view_is(_.MONTH) ? (s = t % (this.options.column_width / 30), i = e - s + (s < this.options.column_width / 60 ? 0 : this.options.column_width / 30)) : (s = t % this.options.column_width, i = e - s + (s < this.options.column_width / 2 ? 0 : this.options.column_width)), i; } unselect_all() { [...this.$svg.querySelectorAll(".bar-wrapper")].forEach((t) => { @@ -1277,14 +1299,14 @@ class B { * @memberof Gantt */ clear() { - var t, e, s, i, o, r; - this.$svg.innerHTML = "", (e = (t = this.$header) == null ? void 0 : t.remove) == null || e.call(t), (i = (s = this.$current_highlight) == null ? void 0 : s.remove) == null || i.call(s), (r = (o = this.popup) == null ? void 0 : o.hide) == null || r.call(o); + var t, e, s, i, o, n; + this.$svg.innerHTML = "", (e = (t = this.$header) == null ? void 0 : t.remove) == null || e.call(t), (i = (s = this.$current_highlight) == null ? void 0 : s.remove) == null || i.call(s), (n = (o = this.popup) == null ? void 0 : o.hide) == null || n.call(o); } } -B.VIEW_MODE = l; -function F(n) { - return n.name + "_" + Math.random().toString(36).slice(2, 12); +I.VIEW_MODE = _; +function B(r) { + return r.name + "_" + Math.random().toString(36).slice(2, 12); } export { - B as default + I as default }; diff --git a/dist/frappe-gantt.umd.js b/dist/frappe-gantt.umd.js index e792746..6b5ef5a 100644 --- a/dist/frappe-gantt.umd.js +++ b/dist/frappe-gantt.umd.js @@ -1,23 +1,23 @@ -(function(y,x){typeof exports=="object"&&typeof module<"u"?module.exports=x():typeof define=="function"&&define.amd?define(x):(y=typeof globalThis<"u"?globalThis:y||self,y.Gantt=x())})(this,function(){"use strict";const y="year",x="month",M="day",D="hour",E="minute",Y="second",L="millisecond",S={January:"Jan",February:"Feb",March:"Mar",April:"Apr",May:"May",June:"Jun",July:"Jul",August:"Aug",September:"Sep",October:"Oct",November:"Nov",December:"Dec"},h={parse_duration(r){const e=/([0-9]+)(y|m|d|h|min|s|ms)/gm.exec(r);if(e!==null){if(e[2]==="y")return{duration:parseInt(e[1]),scale:"year"};if(e[2]==="m")return{duration:parseInt(e[1]),scale:"month"};if(e[2]==="d")return{duration:parseInt(e[1]),scale:"day"};if(e[2]==="h")return{duration:parseInt(e[1]),scale:"hour"};if(e[2]==="min")return{duration:parseInt(e[1]),scale:"minute"};if(e[2]==="s")return{duration:parseInt(e[1]),scale:"second"};if(e[2]==="ms")return{duration:parseInt(e[1]),scale:"millisecond"}}},parse(r,t="-",e=/[.:]/){if(r instanceof Date)return r;if(typeof r=="string"){let s,i;const o=r.split(" ");s=o[0].split(t).map(a=>parseInt(a,10)),i=o[1]&&o[1].split(e),s[1]=s[1]?s[1]-1:0;let n=s;return i&&i.length&&(i.length===4&&(i[3]="0."+i[3],i[3]=parseFloat(i[3])*1e3),n=n.concat(i)),new Date(...n)}},to_string(r,t=!1){if(!(r instanceof Date))throw new TypeError("Invalid argument type");const e=this.get_date_values(r).map((o,n)=>(n===1&&(o=o+1),n===6?k(o+"",3,"0"):k(o+"",2,"0"))),s=`${e[0]}-${e[1]}-${e[2]}`,i=`${e[3]}:${e[4]}:${e[5]}.${e[6]}`;return s+(t?" "+i:"")},format(r,t="YYYY-MM-DD HH:mm:ss.SSS",e="en"){const i=new Intl.DateTimeFormat(e,{month:"long"}).format(r),o=i.charAt(0).toUpperCase()+i.slice(1),n=this.get_date_values(r).map(g=>k(g,2,0)),a={YYYY:n[0],MM:k(+n[1]+1,2,0),DD:n[2],HH:n[3],mm:n[4],ss:n[5],SSS:n[6],D:n[2],MMMM:o,MMM:S[o]};let d=t;const p=[];return Object.keys(a).sort((g,c)=>c.length-g.length).forEach(g=>{d.includes(g)&&(d=d.replaceAll(g,`$${p.length}`),p.push(a[g]))}),p.forEach((g,c)=>{d=d.replaceAll(`$${c}`,g)}),d},diff(r,t,e=M){let s,i,o,n,a,d,p;s=r-t,i=s/1e3,n=i/60,o=n/60,a=o/24;const g=r.getFullYear()-t.getFullYear(),c=r.getMonth()-t.getMonth();return d=g*12+c,r.getDate()>0,e=String(typeof e<"u"?e:" "),r.length>t?String(r):(t=t-r.length,t>e.length&&(e+=e.repeat(t/e.length)),e.slice(0,t)+String(r))}function _(r,t){return typeof r=="string"?(t||document).querySelector(r):r||null}function u(r,t){const e=document.createElementNS("http://www.w3.org/2000/svg",r);for(let s in t)s==="append_to"?t.append_to.appendChild(e):s==="innerHTML"?e.innerHTML=t.innerHTML:s==="clipPath"?e.setAttribute("clip-path","url(#"+t[s]+")"):e.setAttribute(s,t[s]);return e}function A(r,t,e,s){const i=W(r,t,e,s);if(i===r){const o=document.createEvent("HTMLEvents");o.initEvent("click",!0,!0),o.eventName="click",i.dispatchEvent(o)}}function W(r,t,e,s,i="0.4s",o="0.1s"){const n=r.querySelector("animate");if(n)return _.attr(n,{attributeName:t,from:e,to:s,dur:i,begin:"click + "+o}),r;const a=u("animate",{attributeName:t,from:e,to:s,dur:i,begin:o,calcMode:"spline",values:e+";"+s,keyTimes:"0; 1",keySplines:X("ease-out")});return r.appendChild(a),r}function X(r){return{ease:".25 .1 .25 1",linear:"0 0 1 1","ease-in":".42 0 1 1","ease-out":"0 0 .58 1","ease-in-out":".42 0 .58 1"}[r]}_.on=(r,t,e,s)=>{s?_.delegate(r,t,e,s):(s=e,_.bind(r,t,s))},_.off=(r,t,e)=>{r.removeEventListener(t,e)},_.bind=(r,t,e)=>{t.split(/\s+/).forEach(function(s){r.addEventListener(s,e)})},_.delegate=(r,t,e,s)=>{r.addEventListener(t,function(i){const o=i.target.closest(e);o&&(i.delegatedTarget=o,s.call(this,i,o))})},_.closest=(r,t)=>t?t.matches(r)?t:_.closest(r,t.parentNode):null,_.attr=(r,t,e)=>{if(!e&&typeof t=="string")return r.getAttribute(t);if(typeof t=="object"){for(let s in t)_.attr(r,s,t[s]);return}r.setAttribute(t,e)};class O{constructor(t,e){this.set_defaults(t,e),this.prepare(),this.draw(),this.bind()}set_defaults(t,e){this.action_completed=!1,this.gantt=t,this.task=e}prepare(){this.prepare_values(),this.prepare_helpers()}prepare_values(){this.invalid=this.task.invalid,this.height=this.gantt.options.bar_height,this.image_size=this.height-5,this.compute_x(),this.compute_y(),this.compute_duration(),this.corner_radius=this.gantt.options.bar_corner_radius,this.width=this.gantt.options.column_width*this.duration,this.progress_width=this.gantt.options.column_width*this.duration*(this.task.progress/100)||0,this.group=u("g",{class:"bar-wrapper"+(this.task.custom_class?" "+this.task.custom_class:"")+(this.task.important?" important":""),"data-id":this.task.id}),this.bar_group=u("g",{class:"bar-group",append_to:this.group}),this.handle_group=u("g",{class:"handle-group",append_to:this.group})}prepare_helpers(){SVGElement.prototype.getX=function(){return+this.getAttribute("x")},SVGElement.prototype.getY=function(){return+this.getAttribute("y")},SVGElement.prototype.getWidth=function(){return+this.getAttribute("width")},SVGElement.prototype.getHeight=function(){return+this.getAttribute("height")},SVGElement.prototype.getEndX=function(){return this.getX()+this.getWidth()}}prepare_expected_progress_values(){this.compute_expected_progress(),this.expected_progress_width=this.gantt.options.column_width*this.duration*(this.expected_progress/100)||0}draw(){this.draw_bar(),this.draw_progress_bar(),this.gantt.options.show_expected_progress&&(this.prepare_expected_progress_values(),this.draw_expected_progress_bar()),this.draw_label(),this.draw_resize_handles(),this.task.thumbnail&&this.draw_thumbnail()}draw_bar(){this.$bar=u("rect",{x:this.x,y:this.y,width:this.width,height:this.height,rx:this.corner_radius,ry:this.corner_radius,class:"bar"+(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)&&!this.task.important?" safari":""),append_to:this.bar_group}),A(this.$bar,"width",0,this.width),this.invalid&&this.$bar.classList.add("bar-invalid")}draw_expected_progress_bar(){this.invalid||(this.$expected_bar_progress=u("rect",{x:this.x,y:this.y,width:this.expected_progress_width,height:this.height,rx:this.corner_radius,ry:this.corner_radius,class:"bar-expected-progress",append_to:this.bar_group}),A(this.$expected_bar_progress,"width",0,this.expected_progress_width))}draw_progress_bar(){if(this.invalid)return;this.$bar_progress=u("rect",{x:this.x,y:this.y,width:this.progress_width,height:this.height,rx:this.corner_radius,ry:this.corner_radius,class:"bar-progress",append_to:this.bar_group});const t=h.diff(this.task._start,this.gantt.gantt_start,"hour")/this.gantt.options.step*this.gantt.options.column_width;let e=document.createElement("div");e.id=`${this.task.id}-highlight`,e.classList.add("date-highlight"),e.style.height=this.height*.8+"px",e.style.width=this.width+"px",e.style.top=this.gantt.options.header_height-25+"px",e.style.left=t+"px",this.$date_highlight=e,this.gantt.$lower_header.prepend(e),A(this.$bar_progress,"width",0,this.progress_width)}draw_label(){let t=this.x+this.$bar.getWidth()/2;this.task.thumbnail&&(t=this.x+this.image_size+5),u("text",{x:t,y:this.y+this.height/2,innerHTML:this.task.name,class:"bar-label",append_to:this.bar_group}),requestAnimationFrame(()=>this.update_label_position())}draw_thumbnail(){let t=10,e=2,s,i;s=u("defs",{append_to:this.bar_group}),u("rect",{id:"rect_"+this.task.id,x:this.x+t,y:this.y+e,width:this.image_size,height:this.image_size,rx:"15",class:"img_mask",append_to:s}),i=u("clipPath",{id:"clip_"+this.task.id,append_to:s}),u("use",{href:"#rect_"+this.task.id,append_to:i}),u("image",{x:this.x+t,y:this.y+e,width:this.image_size,height:this.image_size,class:"bar-img",href:this.task.thumbnail,clipPath:"clip_"+this.task.id,append_to:this.bar_group})}draw_resize_handles(){if(this.invalid||this.gantt.options.readonly)return;const t=this.$bar,e=8;if(this.gantt.options.dates_readonly||(u("rect",{x:t.getX()+t.getWidth()+e-4,y:t.getY()+1,width:e,height:this.height-2,rx:this.corner_radius,ry:this.corner_radius,class:"handle right",append_to:this.handle_group}),u("rect",{x:t.getX()-e-4,y:t.getY()+1,width:e,height:this.height-2,rx:this.corner_radius,ry:this.corner_radius,class:"handle left",append_to:this.handle_group})),!this.gantt.options.progress_readonly){const s=this.$bar_progress;this.$handle_progress=u("circle",{cx:s.getEndX(),cy:s.getY()+s.getHeight()/2,r:5,class:"handle progress",append_to:this.handle_group})}}bind(){this.invalid||this.setup_click_event()}setup_click_event(){let t=this.task.id;if(_.on(this.group,"mouseover",e=>{this.gantt.trigger_event("hover",[this.task,e.screenX,e.screenY,e])}),this.gantt.options.popup_on==="click"){let e=!1;_.on(this.group,"click",s=>{console.log(e),e?this.gantt.hide_popup():(this.show_popup(s.offsetX||s.layerX),document.getElementById(`${t}-highlight`).style.display="block"),e=!e})}else{let e;_.on(this.group,"mouseenter",s=>e=setTimeout(()=>{this.show_popup(s.offsetX||s.layerX),document.getElementById(`${t}-highlight`).style.display="block"},200)),_.on(this.group,"mouseleave",()=>{var s,i;clearTimeout(e),(i=(s=this.gantt.popup)==null?void 0:s.hide)==null||i.call(s),document.getElementById(`${t}-highlight`).style.display="none"})}_.on(this.group,"click",()=>{this.gantt.trigger_event("click",[this.task])}),_.on(this.group,"dblclick",e=>{this.action_completed||(this.group.classList.remove("active"),this.gantt.popup&&this.gantt.popup.parent.classList.remove("hidden"),this.gantt.trigger_event("double_click",[this.task]))})}show_popup(t){if(this.gantt.bar_being_dragged)return;const e=h.format(this.task._start,"MMM D",this.gantt.options.language),s=h.format(h.add(this.task._end,-1,"second"),"MMM D",this.gantt.options.language),i=`${e} - ${s}
Progress: ${this.task.progress}`;this.gantt.show_popup({x:t,target_element:this.$bar,title:this.task.name,subtitle:i,task:this.task})}update_bar_position({x:t=null,width:e=null}){const s=this.$bar;if(t){if(!this.task.dependencies.map(n=>this.gantt.get_bar(n).$bar.getX()).reduce((n,a)=>t>=a,t)){e=null;return}this.update_attr(s,"x",t),this.$date_highlight.style.left=t+"px"}e&&(this.update_attr(s,"width",e),this.$date_highlight.style.width=e+"px"),this.update_label_position(),this.update_handle_position(),this.gantt.options.show_expected_progress&&(this.date_changed(),this.compute_duration(),this.update_expected_progressbar_position()),this.update_progressbar_position(),this.update_arrow_position()}update_label_position_on_horizontal_scroll({x:t,sx:e}){const s=document.querySelector(".gantt-container"),i=this.group.querySelector(".bar-label"),o=this.group.querySelector(".bar-img")||"",n=this.bar_group.querySelector(".img_mask")||"";let a=this.$bar.getX()+this.$bar.getWidth(),d=i.getX()+t,p=o&&o.getX()+t||0,g=o&&o.getBBox().width+7||7,c=d+i.getBBox().width+7,f=e+s.clientWidth/2;i.classList.contains("big")||(c0&&cthis.$bar.getX()&&t<0&&c>f)&&(i.setAttribute("x",d),o&&(o.setAttribute("x",p),n.setAttribute("x",p)))}date_changed(){let t=!1;const{new_start_date:e,new_end_date:s}=this.compute_start_end_date();Number(this.task._start)!==Number(e)&&(t=!0,this.task._start=e),Number(this.task._end)!==Number(s)&&(t=!0,this.task._end=s),t&&this.gantt.trigger_event("date_change",[this.task,e,h.add(s,-1,"second")])}progress_changed(){const t=this.compute_progress();this.task.progress=t,this.gantt.trigger_event("progress_change",[this.task,t])}set_action_completed(){this.action_completed=!0,setTimeout(()=>this.action_completed=!1,1e3)}compute_start_end_date(){const t=this.$bar,e=t.getX()/this.gantt.options.column_width;let s=h.add(this.gantt.gantt_start,e*this.gantt.options.step,"hour");const i=this.gantt.gantt_start.getTimezoneOffset()-s.getTimezoneOffset();i&&(s=h.add(s,i,"minute"));const o=t.getWidth()/this.gantt.options.column_width,n=h.add(s,o*this.gantt.options.step,"hour");return{new_start_date:s,new_end_date:n}}compute_progress(){const t=this.$bar_progress.getWidth()/this.$bar.getWidth()*100;return parseInt(t,10)}compute_expected_progress(){this.expected_progress=h.diff(h.today(),this.task._start,"hour")/this.gantt.options.step,this.expected_progress=(this.expected_progressd?(s.classList.add("big"),i?(i.setAttribute("x",e.getX()+e.getWidth()+o),t.setAttribute("x",e.getX()+e.getWidth()+o),s.setAttribute("x",e.getX()+e.getWidth()+n)):s.setAttribute("x",e.getX()+e.getWidth()+o)):(s.classList.remove("big"),i?(i.setAttribute("x",e.getX()+o),t.setAttribute("x",e.getX()+o),s.setAttribute("x",e.getX()+d/2+n)):s.setAttribute("x",e.getX()+d/2-a/2))}update_handle_position(){if(this.invalid||this.gantt.options.readonly)return;const t=this.$bar;this.handle_group.querySelector(".handle.left").setAttribute("x",t.getX()-12),this.handle_group.querySelector(".handle.right").setAttribute("x",t.getEndX()+4);const e=this.group.querySelector(".handle.progress");e&&e.setAttribute("cx",this.$bar_progress.getEndX())}update_arrow_position(){this.arrows=this.arrows||[];for(let t of this.arrows)t.update()}}class C{constructor(t,e,s){this.gantt=t,this.from_task=e,this.to_task=s,this.calculate_path(),this.draw()}calculate_path(){let t=this.from_task.$bar.getX()+this.from_task.$bar.getWidth()/2;const e=()=>this.to_task.$bar.getX()this.from_task.$bar.getX()+this.gantt.options.padding;for(;e();)t-=10;const s=this.gantt.options.header_height+this.gantt.options.bar_height+(this.gantt.options.padding+this.gantt.options.bar_height)*this.from_task.task._index+this.gantt.options.padding,i=this.to_task.$bar.getX()-this.gantt.options.padding/2-7,o=this.gantt.options.header_height+this.gantt.options.bar_height/2+(this.gantt.options.padding+this.gantt.options.bar_height)*this.to_task.task._index+this.gantt.options.padding,n=this.from_task.task._index>this.to_task.task._index,a=this.gantt.options.arrow_curve,d=n?1:0,p=n?-a:a,g=n?o+this.gantt.options.arrow_curve:o-this.gantt.options.arrow_curve;if(this.path=` +(function(y,x){typeof exports=="object"&&typeof module<"u"?module.exports=x():typeof define=="function"&&define.amd?define(x):(y=typeof globalThis<"u"?globalThis:y||self,y.Gantt=x())})(this,function(){"use strict";const y="year",x="month",M="day",D="hour",Y="minute",E="second",L="millisecond",S={January:"Jan",February:"Feb",March:"Mar",April:"Apr",May:"May",June:"Jun",July:"Jul",August:"Aug",September:"Sep",October:"Oct",November:"Nov",December:"Dec"},h={parse_duration(r){const e=/([0-9]+)(y|m|d|h|min|s|ms)/gm.exec(r);if(e!==null){if(e[2]==="y")return{duration:parseInt(e[1]),scale:"year"};if(e[2]==="m")return{duration:parseInt(e[1]),scale:"month"};if(e[2]==="d")return{duration:parseInt(e[1]),scale:"day"};if(e[2]==="h")return{duration:parseInt(e[1]),scale:"hour"};if(e[2]==="min")return{duration:parseInt(e[1]),scale:"minute"};if(e[2]==="s")return{duration:parseInt(e[1]),scale:"second"};if(e[2]==="ms")return{duration:parseInt(e[1]),scale:"millisecond"}}},parse(r,t="-",e=/[.:]/){if(r instanceof Date)return r;if(typeof r=="string"){let s,i;const o=r.split(" ");s=o[0].split(t).map(a=>parseInt(a,10)),i=o[1]&&o[1].split(e),s[1]=s[1]?s[1]-1:0;let n=s;return i&&i.length&&(i.length===4&&(i[3]="0."+i[3],i[3]=parseFloat(i[3])*1e3),n=n.concat(i)),new Date(...n)}},to_string(r,t=!1){if(!(r instanceof Date))throw new TypeError("Invalid argument type");const e=this.get_date_values(r).map((o,n)=>(n===1&&(o=o+1),n===6?k(o+"",3,"0"):k(o+"",2,"0"))),s=`${e[0]}-${e[1]}-${e[2]}`,i=`${e[3]}:${e[4]}:${e[5]}.${e[6]}`;return s+(t?" "+i:"")},format(r,t="YYYY-MM-DD HH:mm:ss.SSS",e="en"){const i=new Intl.DateTimeFormat(e,{month:"long"}).format(r),o=i.charAt(0).toUpperCase()+i.slice(1),n=this.get_date_values(r).map(l=>k(l,2,0)),a={YYYY:n[0],MM:k(+n[1]+1,2,0),DD:n[2],HH:n[3],mm:n[4],ss:n[5],SSS:n[6],D:n[2],MMMM:o,MMM:S[o]};let p=t;const d=[];return Object.keys(a).sort((l,u)=>u.length-l.length).forEach(l=>{p.includes(l)&&(p=p.replaceAll(l,`$${d.length}`),d.push(a[l]))}),d.forEach((l,u)=>{p=p.replaceAll(`$${u}`,l)}),p},diff(r,t,e=M){let s,i,o,n,a,p,d;s=r-t,i=s/1e3,n=i/60,o=n/60,a=o/24;const l=r.getFullYear()-t.getFullYear(),u=r.getMonth()-t.getMonth();return p=l*12+u,r.getDate()>0,e=String(typeof e<"u"?e:" "),r.length>t?String(r):(t=t-r.length,t>e.length&&(e+=e.repeat(t/e.length)),e.slice(0,t)+String(r))}function g(r,t){return typeof r=="string"?(t||document).querySelector(r):r||null}function c(r,t){const e=document.createElementNS("http://www.w3.org/2000/svg",r);for(let s in t)s==="append_to"?t.append_to.appendChild(e):s==="innerHTML"?e.innerHTML=t.innerHTML:s==="clipPath"?e.setAttribute("clip-path","url(#"+t[s]+")"):e.setAttribute(s,t[s]);return e}function A(r,t,e,s){const i=W(r,t,e,s);if(i===r){const o=document.createEvent("HTMLEvents");o.initEvent("click",!0,!0),o.eventName="click",i.dispatchEvent(o)}}function W(r,t,e,s,i="0.4s",o="0.1s"){const n=r.querySelector("animate");if(n)return g.attr(n,{attributeName:t,from:e,to:s,dur:i,begin:"click + "+o}),r;const a=c("animate",{attributeName:t,from:e,to:s,dur:i,begin:o,calcMode:"spline",values:e+";"+s,keyTimes:"0; 1",keySplines:X("ease-out")});return r.appendChild(a),r}function X(r){return{ease:".25 .1 .25 1",linear:"0 0 1 1","ease-in":".42 0 1 1","ease-out":"0 0 .58 1","ease-in-out":".42 0 .58 1"}[r]}g.on=(r,t,e,s)=>{s?g.delegate(r,t,e,s):(s=e,g.bind(r,t,s))},g.off=(r,t,e)=>{r.removeEventListener(t,e)},g.bind=(r,t,e)=>{t.split(/\s+/).forEach(function(s){r.addEventListener(s,e)})},g.delegate=(r,t,e,s)=>{r.addEventListener(t,function(i){const o=i.target.closest(e);o&&(i.delegatedTarget=o,s.call(this,i,o))})},g.closest=(r,t)=>t?t.matches(r)?t:g.closest(r,t.parentNode):null,g.attr=(r,t,e)=>{if(!e&&typeof t=="string")return r.getAttribute(t);if(typeof t=="object"){for(let s in t)g.attr(r,s,t[s]);return}r.setAttribute(t,e)};class C{constructor(t,e){this.set_defaults(t,e),this.prepare(),this.draw(),this.bind()}set_defaults(t,e){this.action_completed=!1,this.gantt=t,this.task=e}prepare(){this.prepare_values(),this.prepare_helpers()}prepare_values(){this.invalid=this.task.invalid,this.height=this.gantt.options.bar_height,this.image_size=this.height-5,this.compute_x(),this.compute_y(),this.compute_duration(),this.corner_radius=this.gantt.options.bar_corner_radius,this.width=this.gantt.options.column_width*this.duration,this.progress_width=this.gantt.options.column_width*this.duration*(this.task.progress/100)||0,this.group=c("g",{class:"bar-wrapper"+(this.task.custom_class?" "+this.task.custom_class:"")+(this.task.important?" important":""),"data-id":this.task.id}),this.bar_group=c("g",{class:"bar-group",append_to:this.group}),this.handle_group=c("g",{class:"handle-group",append_to:this.group})}prepare_helpers(){SVGElement.prototype.getX=function(){return+this.getAttribute("x")},SVGElement.prototype.getY=function(){return+this.getAttribute("y")},SVGElement.prototype.getWidth=function(){return+this.getAttribute("width")},SVGElement.prototype.getHeight=function(){return+this.getAttribute("height")},SVGElement.prototype.getEndX=function(){return this.getX()+this.getWidth()}}prepare_expected_progress_values(){this.compute_expected_progress(),this.expected_progress_width=this.gantt.options.column_width*this.duration*(this.expected_progress/100)||0}draw(){this.draw_bar(),this.draw_progress_bar(),this.gantt.options.show_expected_progress&&(this.prepare_expected_progress_values(),this.draw_expected_progress_bar()),this.draw_label(),this.draw_resize_handles(),this.task.thumbnail&&this.draw_thumbnail()}draw_bar(){this.$bar=c("rect",{x:this.x,y:this.y,width:this.width,height:this.height,rx:this.corner_radius,ry:this.corner_radius,class:"bar"+(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)&&!this.task.important?" safari":""),append_to:this.bar_group}),A(this.$bar,"width",0,this.width),this.invalid&&this.$bar.classList.add("bar-invalid")}draw_expected_progress_bar(){this.invalid||(this.$expected_bar_progress=c("rect",{x:this.x,y:this.y,width:this.expected_progress_width,height:this.height,rx:this.corner_radius,ry:this.corner_radius,class:"bar-expected-progress",append_to:this.bar_group}),A(this.$expected_bar_progress,"width",0,this.expected_progress_width))}draw_progress_bar(){if(this.invalid)return;this.$bar_progress=c("rect",{x:this.x,y:this.y,width:this.progress_width,height:this.height,rx:this.corner_radius,ry:this.corner_radius,class:"bar-progress",append_to:this.bar_group});const t=h.diff(this.task._start,this.gantt.gantt_start,"hour")/this.gantt.options.step*this.gantt.options.column_width;let e=document.createElement("div");e.id=`${this.task.id}-highlight`,e.classList.add("date-highlight"),e.style.height=this.height*.8+"px",e.style.width=this.width+"px",e.style.top=this.gantt.options.header_height-25+"px",e.style.left=t+"px",this.$date_highlight=e,this.gantt.$lower_header.prepend(e),A(this.$bar_progress,"width",0,this.progress_width)}draw_label(){let t=this.x+this.$bar.getWidth()/2;this.task.thumbnail&&(t=this.x+this.image_size+5),c("text",{x:t,y:this.y+this.height/2,innerHTML:this.task.name,class:"bar-label",append_to:this.bar_group}),requestAnimationFrame(()=>this.update_label_position())}draw_thumbnail(){let t=10,e=2,s,i;s=c("defs",{append_to:this.bar_group}),c("rect",{id:"rect_"+this.task.id,x:this.x+t,y:this.y+e,width:this.image_size,height:this.image_size,rx:"15",class:"img_mask",append_to:s}),i=c("clipPath",{id:"clip_"+this.task.id,append_to:s}),c("use",{href:"#rect_"+this.task.id,append_to:i}),c("image",{x:this.x+t,y:this.y+e,width:this.image_size,height:this.image_size,class:"bar-img",href:this.task.thumbnail,clipPath:"clip_"+this.task.id,append_to:this.bar_group})}draw_resize_handles(){if(this.invalid||this.gantt.options.readonly)return;const t=this.$bar,e=8;if(this.gantt.options.dates_readonly||(c("rect",{x:t.getX()+t.getWidth()+e-4,y:t.getY()+1,width:e,height:this.height-2,rx:this.corner_radius,ry:this.corner_radius,class:"handle right",append_to:this.handle_group}),c("rect",{x:t.getX()-e-4,y:t.getY()+1,width:e,height:this.height-2,rx:this.corner_radius,ry:this.corner_radius,class:"handle left",append_to:this.handle_group})),!this.gantt.options.progress_readonly){const s=this.$bar_progress;this.$handle_progress=c("circle",{cx:s.getEndX(),cy:s.getY()+s.getHeight()/2,r:5,class:"handle progress",append_to:this.handle_group})}}bind(){this.invalid||this.setup_click_event()}setup_click_event(){let t=this.task.id;if(g.on(this.group,"mouseover",e=>{this.gantt.trigger_event("hover",[this.task,e.screenX,e.screenY,e])}),this.gantt.options.popup_on==="click"){let e=!1;g.on(this.group,"click",s=>{console.log(e),e?this.gantt.hide_popup():(this.show_popup(s.offsetX||s.layerX),document.getElementById(`${t}-highlight`).style.display="block"),e=!e})}else{let e;g.on(this.group,"mouseenter",s=>e=setTimeout(()=>{this.show_popup(s.offsetX||s.layerX),document.getElementById(`${t}-highlight`).style.display="block"},200)),g.on(this.group,"mouseleave",()=>{var s,i;clearTimeout(e),(i=(s=this.gantt.popup)==null?void 0:s.hide)==null||i.call(s),document.getElementById(`${t}-highlight`).style.display="none"})}g.on(this.group,"click",()=>{this.gantt.trigger_event("click",[this.task])}),g.on(this.group,"dblclick",e=>{this.action_completed||(this.group.classList.remove("active"),this.gantt.popup&&this.gantt.popup.parent.classList.remove("hidden"),this.gantt.trigger_event("double_click",[this.task]))})}show_popup(t){if(this.gantt.bar_being_dragged)return;const e=h.format(this.task._start,"MMM D",this.gantt.options.language),s=h.format(h.add(this.task._end,-1,"second"),"MMM D",this.gantt.options.language),i=`${e} - ${s}
Progress: ${this.task.progress}`;this.gantt.show_popup({x:t,target_element:this.$bar,title:this.task.name,subtitle:i,task:this.task})}update_bar_position({x:t=null,width:e=null}){const s=this.$bar;if(t){if(!this.task.dependencies.map(n=>this.gantt.get_bar(n).$bar.getX()).reduce((n,a)=>t>=a,t)){e=null;return}this.update_attr(s,"x",t),this.$date_highlight.style.left=t+"px"}e&&(this.update_attr(s,"width",e),this.$date_highlight.style.width=e+"px"),this.update_label_position(),this.update_handle_position(),this.gantt.options.show_expected_progress&&(this.date_changed(),this.compute_duration(),this.update_expected_progressbar_position()),this.update_progressbar_position(),this.update_arrow_position()}update_label_position_on_horizontal_scroll({x:t,sx:e}){const s=document.querySelector(".gantt-container"),i=this.group.querySelector(".bar-label"),o=this.group.querySelector(".bar-img")||"",n=this.bar_group.querySelector(".img_mask")||"";let a=this.$bar.getX()+this.$bar.getWidth(),p=i.getX()+t,d=o&&o.getX()+t||0,l=o&&o.getBBox().width+7||7,u=p+i.getBBox().width+7,f=e+s.clientWidth/2;i.classList.contains("big")||(u0&&uthis.$bar.getX()&&t<0&&u>f)&&(i.setAttribute("x",p),o&&(o.setAttribute("x",d),n.setAttribute("x",d)))}date_changed(){let t=!1;const{new_start_date:e,new_end_date:s}=this.compute_start_end_date();Number(this.task._start)!==Number(e)&&(t=!0,this.task._start=e),Number(this.task._end)!==Number(s)&&(t=!0,this.task._end=s),t&&this.gantt.trigger_event("date_change",[this.task,e,h.add(s,-1,"second")])}progress_changed(){const t=this.compute_progress();this.task.progress=t,this.gantt.trigger_event("progress_change",[this.task,t])}set_action_completed(){this.action_completed=!0,setTimeout(()=>this.action_completed=!1,1e3)}compute_start_end_date(){const t=this.$bar,e=t.getX()/this.gantt.options.column_width;let s=h.add(this.gantt.gantt_start,e*this.gantt.options.step,"hour");const i=this.gantt.gantt_start.getTimezoneOffset()-s.getTimezoneOffset();i&&(s=h.add(s,i,"minute"));const o=t.getWidth()/this.gantt.options.column_width,n=h.add(s,o*this.gantt.options.step,"hour");return{new_start_date:s,new_end_date:n}}compute_progress(){const t=this.$bar_progress.getWidth()/this.$bar.getWidth()*100;return parseInt(t,10)}compute_expected_progress(){this.expected_progress=h.diff(h.today(),this.task._start,"hour")/this.gantt.options.step,this.expected_progress=(this.expected_progressp?(s.classList.add("big"),i?(i.setAttribute("x",e.getX()+e.getWidth()+o),t.setAttribute("x",e.getX()+e.getWidth()+o),s.setAttribute("x",e.getX()+e.getWidth()+n)):s.setAttribute("x",e.getX()+e.getWidth()+o)):(s.classList.remove("big"),i?(i.setAttribute("x",e.getX()+o),t.setAttribute("x",e.getX()+o),s.setAttribute("x",e.getX()+p/2+n)):s.setAttribute("x",e.getX()+p/2-a/2))}update_handle_position(){if(this.invalid||this.gantt.options.readonly)return;const t=this.$bar;this.handle_group.querySelector(".handle.left").setAttribute("x",t.getX()-12),this.handle_group.querySelector(".handle.right").setAttribute("x",t.getEndX()+4);const e=this.group.querySelector(".handle.progress");e&&e.setAttribute("cx",this.$bar_progress.getEndX())}update_arrow_position(){this.arrows=this.arrows||[];for(let t of this.arrows)t.update()}}class O{constructor(t,e,s){this.gantt=t,this.from_task=e,this.to_task=s,this.calculate_path(),this.draw()}calculate_path(){let t=this.from_task.$bar.getX()+this.from_task.$bar.getWidth()/2;const e=()=>this.to_task.$bar.getX()this.from_task.$bar.getX()+this.gantt.options.padding;for(;e();)t-=10;const s=this.gantt.options.header_height+this.gantt.options.bar_height+(this.gantt.options.padding+this.gantt.options.bar_height)*this.from_task.task._index+this.gantt.options.padding,i=this.to_task.$bar.getX()-this.gantt.options.padding/2-7,o=this.gantt.options.header_height+this.gantt.options.bar_height/2+(this.gantt.options.padding+this.gantt.options.bar_height)*this.to_task.task._index+this.gantt.options.padding,n=this.from_task.task._index>this.to_task.task._index,a=this.gantt.options.arrow_curve,p=n?1:0,d=n?-a:a,l=n?o+this.gantt.options.arrow_curve:o-this.gantt.options.arrow_curve;if(this.path=` M ${t} ${s} - V ${g} - a ${a} ${a} 0 0 ${d} ${a} ${p} + V ${l} + a ${a} ${a} 0 0 ${p} ${a} ${d} L ${i} ${o} m -5 -5 l 5 5 - l -5 5`,this.to_task.$bar.getX()
- `,this.hide(),this.title=this.parent.querySelector(".title"),this.subtitle=this.parent.querySelector(".subtitle"),this.pointer=this.parent.querySelector(".pointer")}show(t){if(!t.target_element)throw new Error("target_element is required to show popup");const e=t.target_element;if(this.custom_html){let i=this.custom_html(t.task);i+='
',this.parent.innerHTML=i,this.pointer=this.parent.querySelector(".pointer")}else this.title.innerHTML=t.title,this.subtitle.innerHTML=t.subtitle;console.log("hey in");let s;e instanceof HTMLElement?s=e.getBoundingClientRect():e instanceof SVGElement&&(s=t.target_element.getBBox()),this.parent.style.left=t.x-this.parent.clientWidth/2+"px",this.parent.style.top=s.y+s.height+10+"px",this.parent.classList.remove("hidden"),this.pointer.style.left=this.parent.clientWidth/2+"px",this.pointer.style.top="-15px",this.parent.style.opacity=1}hide(){this.parent.style.opacity=0,this.parent.style.left=0}}const l={HOUR:"Hour",QUARTER_DAY:"Quarter Day",HALF_DAY:"Half Day",DAY:"Day",WEEK:"Week",MONTH:"Month",YEAR:"Year"},R={HOUR:["7d","7d"],QUARTER_DAY:["7d","7d"],HALF_DAY:["7d","7d"],DAY:["1m","1m"],WEEK:["1m","1m"],MONTH:["1m","1m"],YEAR:["2y","2y"]},I={header_height:65,column_width:30,view_modes:[...Object.values(l)],bar_height:30,bar_corner_radius:3,arrow_curve:5,padding:18,view_mode:"Day",date_format:"YYYY-MM-DD",show_expected_progress:!1,popup:null,popup_on:"hover",language:"en",readonly:!1,progress_readonly:!1,dates_readonly:!1,highlight_weekend:!0,scroll_to:"start",lines:"both",auto_move_label:!0,today_button:!0,view_mode_select:!1};class T{constructor(t,e,s){this.setup_wrapper(t),this.setup_options(s),this.setup_tasks(e),this.change_view_mode(),this.bind_events()}setup_wrapper(t){let e,s;if(typeof t=="string"&&(t=document.querySelector(t)),t instanceof HTMLElement)s=t,e=t.querySelector("svg");else if(t instanceof SVGElement)e=t;else throw new TypeError("Frappe Gantt only supports usage of a string CSS selector, HTML DOM element or SVG DOM element for the 'element' parameter");e?(this.$svg=e,this.$svg.classList.add("gantt")):this.$svg=u("svg",{append_to:s,class:"gantt"}),this.$container=document.createElement("div"),this.$container.classList.add("gantt-container"),this.$svg.parentElement.appendChild(this.$container),this.$container.appendChild(this.$svg),this.$popup_wrapper=document.createElement("div"),this.$popup_wrapper.classList.add("popup-wrapper"),this.$container.appendChild(this.$popup_wrapper)}setup_options(t){this.options={...I,...t},t.view_mode_padding||(t.view_mode_padding={});for(let[e,s]of Object.entries(t.view_mode_padding))typeof s=="string"&&(t.view_mode_padding[e]=[s,s]);this.options.view_mode_padding={...R,...t.view_mode_padding}}setup_tasks(t){this.tasks=t.map((e,s)=>{if(e._start=h.parse(e.start),e.end===void 0&&e.duration!==void 0&&(e.end=e._start,e.duration.split(" ").forEach(a=>{let{duration:d,scale:p}=h.parse_duration(a);e.end=h.add(e.end,d,p)})),e._end=h.parse(e.end),h.diff(e._end,e._start,"year")<0)throw Error("start of task can't be after end of task: in task #, "+(s+1));if(h.diff(e._end,e._start,"year")>10&&(e.end=null),e._index=s,!e.start&&!e.end){const n=h.today();e._start=n,e._end=h.add(n,2,"day")}if(!e.start&&e.end&&(e._start=h.add(e._end,-2,"day")),e.start&&!e.end&&(e._end=h.add(e._start,2,"day")),h.get_date_values(e._end).slice(3).every(n=>n===0)&&(e._end=h.add(e._end,24,"hour")),(!e.start||!e.end)&&(e.invalid=!0),typeof e.dependencies=="string"||!e.dependencies){let n=[];e.dependencies&&(n=e.dependencies.split(",").map(a=>a.trim().replaceAll(" ","_")).filter(a=>a)),e.dependencies=n}return e.id?typeof e.id=="string"?e.id=e.id.replaceAll(" ","_"):e.id=`${e.id}`:e.id=B(e),e}),this.setup_dependencies()}setup_dependencies(){this.dependency_map={};for(let t of this.tasks)for(let e of t.dependencies)this.dependency_map[e]=this.dependency_map[e]||[],this.dependency_map[e].push(t.id)}refresh(t){this.setup_tasks(t),this.change_view_mode()}change_view_mode(t=this.options.view_mode){this.update_view_scale(t),this.setup_dates(),this.render(),this.trigger_event("view_change",[t])}update_view_scale(t){this.options.view_mode=t,t===l.HOUR?(this.options.step=24/24,this.options.column_width=38):t===l.DAY?(this.options.step=24,this.options.column_width=38):t===l.HALF_DAY?(this.options.step=24/2,this.options.column_width=38):t===l.QUARTER_DAY?(this.options.step=24/4,this.options.column_width=38):t===l.WEEK?(this.options.step=24*7,this.options.column_width=140):t===l.MONTH?(this.options.step=24*30,this.options.column_width=120):t===l.YEAR&&(this.options.step=24*365,this.options.column_width=120)}setup_dates(){this.setup_gantt_dates(),this.setup_date_values()}setup_gantt_dates(){this.gantt_start=this.gantt_end=null;for(let a of this.tasks)(!this.gantt_start||a._startthis.gantt_end)&&(this.gantt_end=a._end);let t,e;this.gantt_start?t=h.start_of(this.gantt_start,"day"):t=new Date,this.gantt_end?e=h.start_of(this.gantt_end,"day"):e=new Date;let s;for(let[a,d]of Object.entries(l))d===this.options.view_mode&&(s=a);const[i,o]=this.options.view_mode_padding[s].map(h.parse_duration);t=h.add(t,-i.duration,i.scale);let n;this.view_is(l.YEAR)?n="YYYY":this.view_is(l.MONTH)?n="YYYY-MM":this.view_is(l.DAY)?n="YYYY-MM-DD":n="YYYY-MM-DD HH",this.gantt_start=h.parse(h.format(t,n)),this.gantt_start.setHours(0,0,0,0),this.gantt_end=h.add(e,o.duration,o.scale)}setup_date_values(){this.dates=[];let t=null;for(;t===null||tt.bottom?(this.$side_header.style.position="absolute",this.$side_header.style.top=`${t.scrollTop+e.top}px`):(this.$side_header.style.position="fixed",this.$side_header.style.top=i+10+"px");const o=Math.min(this.$header.clientWidth,this.$container.clientWidth);this.$side_header.style.left=s+this.$container.scrollLeft+o-this.$side_header.clientWidth+"px",this.$today_button&&(this.$today_button.style.left=`${t.left+20}px`)}make_grid_ticks(){if(!["both","vertical","horizontal"].includes(this.options.lines))return;let t=0,e=this.options.header_height+this.options.padding/2,s=(this.options.bar_height+this.options.padding)*this.tasks.length,i=u("g",{class:"lines_layer",append_to:this.layers.grid}),o=this.options.header_height+this.options.padding/2;const n=this.dates.length*this.options.column_width,a=this.options.bar_height+this.options.padding;if(this.options.lines!=="vertical")for(let d of this.tasks)u("line",{x1:0,y1:o+a,x2:n,y2:o+a,class:"row-line",append_to:i}),o+=a;if(this.options.lines!=="horizontal")for(let d of this.dates){let p="tick";this.view_is(l.DAY)&&d.getDate()===1&&(p+=" thick"),this.view_is(l.WEEK)&&d.getDate()>=1&&d.getDate()<8&&(p+=" thick"),this.view_is(l.MONTH)&&d.getMonth()%3===0&&(p+=" thick"),u("path",{d:`M ${t} ${e} v ${s}`,class:p,append_to:this.layers.grid}),this.view_is(l.MONTH)?t+=h.get_days_in_month(d)*this.options.column_width/30:t+=this.options.column_width}}highlightWeekends(){if(!(!this.view_is("Day")&&!this.view_is("Half Day"))){for(let t=new Date(this.gantt_start);t<=this.gantt_end;t.setDate(t.getDate()+1))if(t.getDay()===0||t.getDay()===6){const e=h.diff(t,this.gantt_start,"hour")/this.options.step*this.options.column_width,s=(this.options.bar_height+this.options.padding)*this.tasks.length;u("rect",{x:e,y:this.options.header_height+this.options.padding/2,width:(this.view_is("Day")?1:2)*this.options.column_width,height:s,class:"holiday-highlight",append_to:this.layers.grid})}}}computeGridHighlightDimensions(t){let e=this.options.column_width/2;if(this.view_is(l.DAY)){let s=h.today();return{x:e+h.diff(s,this.gantt_start,"hour")/this.options.step*this.options.column_width,date:s}}for(let s of this.dates){const i=new Date,o=new Date(s),n=new Date(s);switch(t){case l.WEEK:n.setDate(s.getDate()+7);break;case l.MONTH:n.setMonth(s.getMonth()+1);break;case l.YEAR:n.setFullYear(s.getFullYear()+1);break}if(i>=o&&i<=n)return{x:e,date:o};e+=this.options.column_width}return{x:e}}make_grid_highlights(){if(this.options.highlight_weekend&&this.highlightWeekends(),this.view_is(l.DAY)||this.view_is(l.WEEK)||this.view_is(l.MONTH)||this.view_is(l.YEAR)){const{x:t,date:e}=this.computeGridHighlightDimensions(this.options.view_mode);if(!e||!this.dates.find(n=>n.getTime()==e.getTime()))return;const s=this.options.header_height+this.options.padding/2,i=(this.options.bar_height+this.options.padding)*this.tasks.length;this.$current_highlight=this.create_el({top:s,left:t,height:i,classes:"current-highlight",append_to:this.$container});let o=document.getElementById(h.format(e).replaceAll(" ","_"));o&&(o.classList.add("current-date-highlight"),o.style.top=+o.style.top.slice(0,-2)-4+"px",o.style.left=+o.style.left.slice(0,-2)-8+"px")}}create_el({left:t,top:e,width:s,height:i,id:o,classes:n,append_to:a}){let d=document.createElement("div");return d.classList.add(n),d.style.top=e+"px",d.style.left=t+"px",o&&(d.id=o),s&&(d.style.width=i+"px"),i&&(d.style.height=i+"px"),a.appendChild(d),d}make_dates(){this.upper_texts_x={},this.get_dates_to_draw().forEach((t,e)=>{let s=this.create_el({left:t.lower_x,top:t.lower_y,id:t.formatted_date,classes:"lower-text",append_to:this.$lower_header});if(s.innerText=t.lower_text,s.style.left=+s.style.left.slice(0,-2)+"px",t.upper_text){this.upper_texts_x[t.upper_text]=t.upper_x;let i=document.createElement("div");i.classList.add("upper-text"),i.style.left=t.upper_x+"px",i.style.top=t.upper_y+"px",i.innerText=t.upper_text,this.$upper_header.appendChild(i),t.upper_x>this.layers.grid.getBBox().width&&i.remove()}})}get_dates_to_draw(){let t=null;return this.dates.map((s,i)=>{const o=this.get_date_info(s,t,i);return t=o,o})}get_date_info(t,e){let s=e?e.date:h.add(t,1,"day");const i={Hour_lower:h.format(t,"HH",this.options.language),"Quarter Day_lower":h.format(t,"HH",this.options.language),"Half Day_lower":h.format(t,"HH",this.options.language),Day_lower:t.getDate()!==s.getDate()?h.format(t,"D",this.options.language):"",Week_lower:t.getMonth()!==s.getMonth()?h.format(t,"D MMM",this.options.language):h.format(t,"D",this.options.language),Month_lower:h.format(t,"MMMM",this.options.language),Year_lower:h.format(t,"YYYY",this.options.language),Hour_upper:t.getDate()!==s.getDate()?h.format(t,"D MMMM",this.options.language):"","Quarter Day_upper":t.getDate()!==s.getDate()?h.format(t,"D MMM",this.options.language):"","Half Day_upper":t.getDate()!==s.getDate()?t.getMonth()!==s.getMonth()?h.format(t,"D MMM",this.options.language):h.format(t,"D",this.options.language):"",Day_upper:t.getMonth()!==s.getMonth()||!e?h.format(t,"MMMM",this.options.language):"",Week_upper:t.getMonth()!==s.getMonth()?h.format(t,"MMMM",this.options.language):"",Month_upper:t.getFullYear()!==s.getFullYear()?h.format(t,"YYYY",this.options.language):"",Year_upper:t.getFullYear()!==s.getFullYear()?h.format(t,"YYYY",this.options.language):""};let o=this.view_is(l.MONTH)?h.get_days_in_month(t)*this.options.column_width/30:this.options.column_width;const n={x:e?e.base_pos_x+e.column_width:0,lower_y:this.options.header_height-20,upper_y:this.options.header_height-50},a={Hour_lower:o/2,Hour_upper:o*12,"Quarter Day_lower":o/2,"Quarter Day_upper":o*2,"Half Day_lower":o/2,"Half Day_upper":o,Day_lower:o/2,Day_upper:o/2,Week_lower:o/2,Week_upper:o*4/2,Month_lower:o/2,Month_upper:o/2,Year_lower:o/2,Year_upper:o*30/2};return{date:t,formatted_date:h.format(t).replaceAll(" ","_"),column_width:o,base_pos_x:n.x,upper_text:this.options.lower_text?this.options.upper_text(t,this.options.view_mode,i[`${this.options.view_mode}_upper`]):i[`${this.options.view_mode}_upper`],lower_text:this.options.lower_text?this.options.lower_text(t,this.options.view_mode,i[`${this.options.view_mode}_lower`]):i[`${this.options.view_mode}_lower`],upper_x:n.x+a[`${this.options.view_mode}_upper`],upper_y:n.upper_y,lower_x:n.x+a[`${this.options.view_mode}_lower`],lower_y:n.lower_y}}make_bars(){this.bars=this.tasks.map(t=>{const e=new O(this,t);return this.layers.bar.appendChild(e.group),e})}make_arrows(){this.arrows=[];for(let t of this.tasks){let e=[];e=t.dependencies.map(s=>{const i=this.get_task(s);if(!i)return;const o=new C(this,this.bars[i._index],this.bars[t._index]);return this.layers.arrow.appendChild(o.element),o}).filter(Boolean),this.arrows=this.arrows.concat(e)}}map_arrows_on_bars(){for(let t of this.bars)t.arrows=this.arrows.filter(e=>e.from_task.task.id===t.task.id||e.to_task.task.id===t.task.id)}set_width(){const t=this.$svg.getBoundingClientRect().width,e=this.$svg.querySelector(".grid .grid-row")?this.$svg.querySelector(".grid .grid-row").getAttribute("width"):0;t{this.unselect_all(),this.hide_popup()})}bind_bar_events(){let t=!1,e=0,s=0,i=!1,o=!1,n=null,a=[];this.bar_being_dragged=null;function d(){return t||i||o}this.$svg.onclick=p=>{p.target.classList.contains("grid-row")&&this.unselect_all()},_.on(this.$svg,"mousedown",".bar-wrapper, .handle",(p,g)=>{const c=_.closest(".bar-wrapper",g);a.forEach(w=>w.group.classList.remove("active")),g.classList.contains("left")?i=!0:g.classList.contains("right")?o=!0:g.classList.contains("bar-wrapper")&&(t=!0),c.classList.add("active"),this.popup&&this.popup.parent.classList.add("hidden"),this.popup&&this.popup.parent.classList.add("hidden"),e=p.offsetX||p.layerX,p.offsetY||p.layerY,n=c.getAttribute("data-id"),a=[n,...this.get_all_dependent_tasks(n)].map(w=>this.get_bar(w)),this.bar_being_dragged=n,a.forEach(w=>{const b=w.$bar;b.ox=b.getX(),b.oy=b.getY(),b.owidth=b.getWidth(),b.finaldx=0})}),_.on(this.$container,"scroll",p=>{let g=document.querySelectorAll(".bar-wrapper"),c=[];const f=[];let w;s&&(w=p.currentTarget.scrollLeft-s);const b=p.currentTarget.scrollLeft/this.options.column_width*this.options.step/24;let $="D MMM";["Year","Month"].includes(this.options.view_mode)?$="YYYY":["Day","Week"].includes(this.options.view_mode)?$="MMMM":this.view_is("Half Day")?$="D":this.view_is("Hour")&&($="D MMMM");let F=h.format(h.add(this.gantt_start,b,"day"),$);const v=Array.from(document.querySelectorAll(".upper-text")).find(m=>m.textContent===F);if(v&&!v.classList.contains("current-upper")){const m=document.querySelector(".current-upper");m&&(m.classList.remove("current-upper"),m.style.left=this.upper_texts_x[m.textContent]+"px",m.style.top=this.options.header_height-50+"px"),v.classList.add("current-upper");let H=this.$svg.getBoundingClientRect();v.style.left=H.x+this.$container.scrollLeft+10+"px",v.style.top=H.y+this.options.header_height-50+"px"}Array.prototype.forEach.call(g,function(m,H){f.push(m.getAttribute("data-id"))}),w&&(c=f.map(m=>this.get_bar(m)),this.options.auto_move_label&&c.forEach(m=>{m.update_label_position_on_horizontal_scroll({x:w,sx:p.currentTarget.scrollLeft})})),s=p.currentTarget.scrollLeft}),_.on(this.$svg,"mousemove",p=>{if(!d())return;const g=(p.offsetX||p.layerX)-e;a.forEach(c=>{const f=c.$bar;f.finaldx=this.get_snap_position(g),this.hide_popup(),i?n===c.task.id?c.update_bar_position({x:f.ox+f.finaldx,width:f.owidth-f.finaldx}):c.update_bar_position({x:f.ox+f.finaldx}):o?n===c.task.id&&c.update_bar_position({width:f.owidth+f.finaldx}):t&&!this.options.readonly&&!this.options.dates_readonly&&c.update_bar_position({x:f.ox+f.finaldx})})}),document.addEventListener("mouseup",p=>{t=!1,i=!1,o=!1}),_.on(this.$svg,"mouseup",p=>{this.bar_being_dragged=null,a.forEach(g=>{g.$bar.finaldx&&(g.date_changed(),g.set_action_completed())})}),this.bind_bar_progress()}bind_bar_progress(){let t=0,e=null,s=null,i=null,o=null;_.on(this.$svg,"mousedown",".handle.progress",(n,a)=>{e=!0,t=n.offsetX||n.layerX,n.offsetY||n.layerY;const p=_.closest(".bar-wrapper",a).getAttribute("data-id");s=this.get_bar(p),i=s.$bar_progress,o=s.$bar,i.finaldx=0,i.owidth=i.getWidth(),i.min_dx=-i.getWidth(),i.max_dx=o.getWidth()-i.getWidth()}),_.on(this.$svg,"mousemove",n=>{if(!e)return;let a=(n.offsetX||n.layerX)-t;a>i.max_dx&&(a=i.max_dx),a{e=!1,i&&i.finaldx&&(i.finaldx=0,s.progress_changed(),s.set_action_completed(),s=null,i=null,o=null)})}get_all_dependent_tasks(t){let e=[],s=[t];for(;s.length;){const i=s.reduce((o,n)=>(o=o.concat(this.dependency_map[n]),o),[]);e=e.concat(i),s=i.filter(o=>!s.includes(o))}return e.filter(Boolean)}get_snap_position(t){let e=t,s,i;return this.view_is(l.WEEK)?(s=t%(this.options.column_width/7),i=e-s+(s{t.classList.remove("active")}),this.popup&&this.popup.parent.classList.remove("hidden")}view_is(t){return typeof t=="string"?this.options.view_mode===t:Array.isArray(t)?t.some(e=>this.options.view_mode===e):!1}get_task(t){return this.tasks.find(e=>e.id===t)}get_bar(t){return this.bars.find(e=>e.task.id===t)}show_popup(t){this.options.popup!==!1&&(this.popup||(this.popup=new N(this.$popup_wrapper,this.options.popup)),this.popup.show(t))}hide_popup(){this.popup&&this.popup.hide()}trigger_event(t,e){this.options["on_"+t]&&this.options["on_"+t].apply(this,e)}get_oldest_starting_date(){return this.tasks.length?this.tasks.map(t=>t._start).reduce((t,e)=>e<=t?e:t):new Date}clear(){var t,e,s,i,o,n;this.$svg.innerHTML="",(e=(t=this.$header)==null?void 0:t.remove)==null||e.call(t),(i=(s=this.$current_highlight)==null?void 0:s.remove)==null||i.call(s),(n=(o=this.popup)==null?void 0:o.hide)==null||n.call(o)}}T.VIEW_MODE=l;function B(r){return r.name+"_"+Math.random().toString(36).slice(2,12)}return T}); + `,this.hide(),this.title=this.parent.querySelector(".title"),this.subtitle=this.parent.querySelector(".subtitle"),this.pointer=this.parent.querySelector(".pointer")}show(t){if(!t.target_element)throw new Error("target_element is required to show popup");const e=t.target_element;if(this.custom_html){let i=this.custom_html(t.task);i+='
',this.parent.innerHTML=i,this.pointer=this.parent.querySelector(".pointer")}else this.title.innerHTML=t.title,this.subtitle.innerHTML=t.subtitle;console.log("hey in");let s;e instanceof HTMLElement?s=e.getBoundingClientRect():e instanceof SVGElement&&(s=t.target_element.getBBox()),this.parent.style.left=t.x-this.parent.clientWidth/2+"px",this.parent.style.top=s.y+s.height+10+"px",this.parent.classList.remove("hidden"),this.pointer.style.left=this.parent.clientWidth/2+"px",this.pointer.style.top="-15px",this.parent.style.opacity=1}hide(){this.parent.style.opacity=0,this.parent.style.left=0}}const _={HOUR:"Hour",QUARTER_DAY:"Quarter Day",HALF_DAY:"Half Day",DAY:"Day",WEEK:"Week",MONTH:"Month",YEAR:"Year"},R={HOUR:["7d","7d"],QUARTER_DAY:["7d","7d"],HALF_DAY:["7d","7d"],DAY:["1m","1m"],WEEK:["1m","1m"],MONTH:["1m","1m"],YEAR:["2y","2y"]},F={header_height:65,column_width:30,view_modes:[...Object.values(_)],bar_height:30,bar_corner_radius:3,arrow_curve:5,padding:18,view_mode:"Day",date_format:"YYYY-MM-DD",show_expected_progress:!1,popup:null,popup_on:"hover",language:"en",readonly:!1,progress_readonly:!1,dates_readonly:!1,highlight_weekend:!0,scroll_to:"start",lines:"both",auto_move_label:!0,today_button:!0,view_mode_select:!1};class T{constructor(t,e,s){this.setup_wrapper(t),this.setup_options(s),this.setup_tasks(e),this.change_view_mode(),this.bind_events()}setup_wrapper(t){let e,s;if(typeof t=="string"&&(t=document.querySelector(t)),t instanceof HTMLElement)s=t,e=t.querySelector("svg");else if(t instanceof SVGElement)e=t;else throw new TypeError("Frappe Gantt only supports usage of a string CSS selector, HTML DOM element or SVG DOM element for the 'element' parameter");e?(this.$svg=e,this.$svg.classList.add("gantt")):this.$svg=c("svg",{append_to:s,class:"gantt"}),this.$container=document.createElement("div"),this.$container.classList.add("gantt-container"),this.$svg.parentElement.appendChild(this.$container),this.$container.appendChild(this.$svg),this.$popup_wrapper=document.createElement("div"),this.$popup_wrapper.classList.add("popup-wrapper"),this.$container.appendChild(this.$popup_wrapper)}setup_options(t){this.options={...F,...t};const e=this.options.custom_view_modes?this.options.custom_view_modes.find(s=>s.name===this.options.view_mode):null;e&&(this.options={...this.options,custom_mode:e}),this.options.view_mode_padding||(this.options.view_mode_padding={});for(let[s,i]of Object.entries(this.options.view_mode_padding))typeof i=="string"&&(this.options.view_mode_padding[s]=[i,i]);this.options.view_mode_padding={...R,...t.view_mode_padding}}setup_tasks(t){this.tasks=t.map((e,s)=>{if(e._start=h.parse(e.start),e.end===void 0&&e.duration!==void 0&&(e.end=e._start,e.duration.split(" ").forEach(a=>{let{duration:p,scale:d}=h.parse_duration(a);e.end=h.add(e.end,p,d)})),e._end=h.parse(e.end),h.diff(e._end,e._start,"year")<0)throw Error("start of task can't be after end of task: in task #, "+(s+1));if(h.diff(e._end,e._start,"year")>10&&(e.end=null),e._index=s,!e.start&&!e.end){const n=h.today();e._start=n,e._end=h.add(n,2,"day")}if(!e.start&&e.end&&(e._start=h.add(e._end,-2,"day")),e.start&&!e.end&&(e._end=h.add(e._start,2,"day")),h.get_date_values(e._end).slice(3).every(n=>n===0)&&(e._end=h.add(e._end,24,"hour")),(!e.start||!e.end)&&(e.invalid=!0),typeof e.dependencies=="string"||!e.dependencies){let n=[];e.dependencies&&(n=e.dependencies.split(",").map(a=>a.trim().replaceAll(" ","_")).filter(a=>a)),e.dependencies=n}return e.id?typeof e.id=="string"?e.id=e.id.replaceAll(" ","_"):e.id=`${e.id}`:e.id=I(e),e}),this.setup_dependencies()}setup_dependencies(){this.dependency_map={};for(let t of this.tasks)for(let e of t.dependencies)this.dependency_map[e]=this.dependency_map[e]||[],this.dependency_map[e].push(t.id)}refresh(t){this.setup_tasks(t),this.change_view_mode()}change_view_mode(t=this.options.view_mode){this.update_view_scale(t),this.setup_dates(),this.render(),this.trigger_event("view_change",[t])}update_view_scale(t){this.options.view_mode=t;const e=this.options.custom_mode;e&&(e.unit==="hour"?(this.options.step=e.step,this.options.column_width=38):e.unit==="day"?(this.options.step=e.step*24,this.options.column_width=38):e.unit==="month"?(this.options.step=e.step*24*30,this.options.column_width=120):(this.options.step=24,this.options.column_width=38)),t===_.HOUR?(this.options.step=24/24,this.options.column_width=38):t===_.DAY?(this.options.step=24,this.options.column_width=38):t===_.HALF_DAY?(this.options.step=24/2,this.options.column_width=38):t===_.QUARTER_DAY?(this.options.step=24/4,this.options.column_width=38):t===_.WEEK?(this.options.step=24*7,this.options.column_width=140):t===_.MONTH?(this.options.step=24*30,this.options.column_width=120):t===_.YEAR&&(this.options.step=24*365,this.options.column_width=120)}setup_dates(){this.setup_gantt_dates(),this.setup_date_values()}setup_gantt_dates(){this.gantt_start=this.gantt_end=null;for(let a of this.tasks)(!this.gantt_start||a._startthis.gantt_end)&&(this.gantt_end=a._end);let t,e;this.gantt_start?t=h.start_of(this.gantt_start,"day"):t=new Date,this.gantt_end?e=h.start_of(this.gantt_end,"day"):e=new Date;const s=this.options.custom_mode;let[i,o]=[{duration:1,scale:"day"},{duration:1,scale:"day"}];if(s)[i,o]=[s.padding,s.padding].map(h.parse_duration);else{let a;for(let[p,d]of Object.entries(_))d===this.options.view_mode&&(a=p);[i,o]=this.options.view_mode_padding[a].map(h.parse_duration)}t=h.add(t,-i.duration,i.scale);let n;this.view_is(_.YEAR)?n="YYYY":this.view_is(_.MONTH)?n="YYYY-MM":this.view_is(_.DAY)?n="YYYY-MM-DD":n="YYYY-MM-DD HH",this.gantt_start=h.parse(h.format(t,n)),this.gantt_start.setHours(0,0,0,0),this.gantt_end=h.add(e,o.duration,o.scale)}setup_date_values(){this.dates=[];let t=null;for(;t===null||tt.bottom?(this.$side_header.style.position="absolute",this.$side_header.style.top=`${t.scrollTop+e.top}px`):(this.$side_header.style.position="fixed",this.$side_header.style.top=i+10+"px");const o=Math.min(this.$header.clientWidth,this.$container.clientWidth);this.$side_header.style.left=s+this.$container.scrollLeft+o-this.$side_header.clientWidth+"px",this.$today_button&&(this.$today_button.style.left=`${t.left+20}px`)}make_grid_ticks(){if(!["both","vertical","horizontal"].includes(this.options.lines))return;let t=0,e=this.options.header_height+this.options.padding/2,s=(this.options.bar_height+this.options.padding)*this.tasks.length,i=c("g",{class:"lines_layer",append_to:this.layers.grid}),o=this.options.header_height+this.options.padding/2;const n=this.dates.length*this.options.column_width,a=this.options.bar_height+this.options.padding;if(this.options.lines!=="vertical")for(let p of this.tasks)c("line",{x1:0,y1:o+a,x2:n,y2:o+a,class:"row-line",append_to:i}),o+=a;if(this.options.lines!=="horizontal")for(let p of this.dates){let d="tick";this.view_is(_.DAY)&&p.getDate()===1&&(d+=" thick"),this.view_is(_.WEEK)&&p.getDate()>=1&&p.getDate()<8&&(d+=" thick"),this.view_is(_.MONTH)&&p.getMonth()%3===0&&(d+=" thick"),c("path",{d:`M ${t} ${e} v ${s}`,class:d,append_to:this.layers.grid}),this.view_is(_.MONTH)?t+=h.get_days_in_month(p)*this.options.column_width/30:t+=this.options.column_width}}highlightWeekends(){if(!(!this.view_is("Day")&&!this.view_is("Half Day"))){for(let t=new Date(this.gantt_start);t<=this.gantt_end;t.setDate(t.getDate()+1))if(t.getDay()===0||t.getDay()===6){const e=h.diff(t,this.gantt_start,"hour")/this.options.step*this.options.column_width,s=(this.options.bar_height+this.options.padding)*this.tasks.length;c("rect",{x:e,y:this.options.header_height+this.options.padding/2,width:(this.view_is("Day")?1:2)*this.options.column_width,height:s,class:"holiday-highlight",append_to:this.layers.grid})}}}computeGridHighlightDimensions(t){let e=this.options.column_width/2;if(this.view_is(_.DAY)){let s=h.today();return{x:e+h.diff(s,this.gantt_start,"hour")/this.options.step*this.options.column_width,date:s}}for(let s of this.dates){const i=new Date,o=new Date(s),n=new Date(s);switch(t){case _.WEEK:n.setDate(s.getDate()+7);break;case _.MONTH:n.setMonth(s.getMonth()+1);break;case _.YEAR:n.setFullYear(s.getFullYear()+1);break}if(i>=o&&i<=n)return{x:e,date:o};e+=this.options.column_width}return{x:e}}make_grid_highlights(){if(this.options.highlight_weekend&&this.highlightWeekends(),this.view_is(_.DAY)||this.view_is(_.WEEK)||this.view_is(_.MONTH)||this.view_is(_.YEAR)){const{x:t,date:e}=this.computeGridHighlightDimensions(this.options.view_mode);if(!e||!this.dates.find(n=>n.getTime()==e.getTime()))return;const s=this.options.header_height+this.options.padding/2,i=(this.options.bar_height+this.options.padding)*this.tasks.length;this.$current_highlight=this.create_el({top:s,left:t,height:i,classes:"current-highlight",append_to:this.$container});let o=document.getElementById(h.format(e).replaceAll(" ","_"));o&&(o.classList.add("current-date-highlight"),o.style.top=+o.style.top.slice(0,-2)-4+"px",o.style.left=+o.style.left.slice(0,-2)-8+"px")}}create_el({left:t,top:e,width:s,height:i,id:o,classes:n,append_to:a}){let p=document.createElement("div");return p.classList.add(n),p.style.top=e+"px",p.style.left=t+"px",o&&(p.id=o),s&&(p.style.width=i+"px"),i&&(p.style.height=i+"px"),a.appendChild(p),p}make_dates(){this.upper_texts_x={},this.get_dates_to_draw().forEach((t,e)=>{let s=this.create_el({left:t.lower_x,top:t.lower_y,id:t.formatted_date,classes:"lower-text",append_to:this.$lower_header});if(s.innerText=t.lower_text,s.style.left=+s.style.left.slice(0,-2)+"px",t.upper_text){this.upper_texts_x[t.upper_text]=t.upper_x;let i=document.createElement("div");i.classList.add("upper-text"),i.style.left=t.upper_x+"px",i.style.top=t.upper_y+"px",i.innerText=t.upper_text,this.$upper_header.appendChild(i),t.upper_x>this.layers.grid.getBBox().width&&i.remove()}})}get_dates_to_draw(){let t=null;return this.dates.map((s,i)=>{const o=this.get_date_info(s,t,i);return t=o,o})}get_date_info(t,e){let s=e?e.date:h.add(t,1,"day"),i={};const o=this.options.custom_mode;if(o){let d,l;const u=o?o.unit.toLowerCase():"day";u==="hour"?(d=h.format(t,"HH",this.options.language),l=t.getDate()!==s.getDate()?h.format(t,"D MMMM",this.options.language):""):u==="day"?(d=t.getDate()!==s.getDate()?h.format(t,"D",this.options.language):"",l=t.getMonth()!==s.getMonth()||!e?h.format(t,"MMMM",this.options.language):""):u==="month"?(d=h.format(t,"MMMM",this.options.language),l=t.getFullYear()!==s.getFullYear()?h.format(t,"YYYY",this.options.language):""):(d=h.format(t,"YYYY",this.options.language),l=""),i[`${o.name}_upper`]=l,i[`${o.name}_lower`]=d}else i={Hour_lower:h.format(t,"HH",this.options.language),"Quarter Day_lower":h.format(t,"HH",this.options.language),"Half Day_lower":h.format(t,"HH",this.options.language),Day_lower:t.getDate()!==s.getDate()?h.format(t,"D",this.options.language):"",Week_lower:t.getMonth()!==s.getMonth()?h.format(t,"D MMM",this.options.language):h.format(t,"D",this.options.language),Month_lower:h.format(t,"MMMM",this.options.language),Year_lower:h.format(t,"YYYY",this.options.language),Hour_upper:t.getDate()!==s.getDate()?h.format(t,"D MMMM",this.options.language):"","Quarter Day_upper":t.getDate()!==s.getDate()?h.format(t,"D MMM",this.options.language):"","Half Day_upper":t.getDate()!==s.getDate()?t.getMonth()!==s.getMonth()?h.format(t,"D MMM",this.options.language):h.format(t,"D",this.options.language):"",Day_upper:t.getMonth()!==s.getMonth()||!e?h.format(t,"MMMM",this.options.language):"",Week_upper:t.getMonth()!==s.getMonth()?h.format(t,"MMMM",this.options.language):"",Month_upper:t.getFullYear()!==s.getFullYear()?h.format(t,"YYYY",this.options.language):"",Year_upper:t.getFullYear()!==s.getFullYear()?h.format(t,"YYYY",this.options.language):""};let n=o&&o.lower_text&&o.lower_text.column_width?o.lower_text.column_width*(o.step??1):this.view_is(_.MONTH)?h.get_days_in_month(t)*this.options.column_width/30:this.options.column_width;const a={x:e?e.base_pos_x+e.column_width:0,lower_y:this.options.header_height-20,upper_y:this.options.header_height-50},p={Hour_lower:n/2,Hour_upper:n*12,"Quarter Day_lower":n/2,"Quarter Day_upper":n*2,"Half Day_lower":n/2,"Half Day_upper":n,Day_lower:n/2,Day_upper:n/2,Week_lower:n/2,Week_upper:n*4/2,Month_lower:n/2,Month_upper:n/2,Year_lower:n/2,Year_upper:n*30/2};return o&&(p[`${o.name}_upper`]=n/2,p[`${o.name}_lower`]=n/(o.unit.toLowerCase()==="day"?1:2)),{date:t,formatted_date:h.format(t).replaceAll(" ","_"),column_width:n,base_pos_x:a.x,upper_text:this.options.lower_text?this.options.upper_text(t,this.options.view_mode,i[`${this.options.view_mode}_upper`]):i[`${this.options.view_mode}_upper`],lower_text:this.options.lower_text?this.options.lower_text(t,this.options.view_mode,i[`${this.options.view_mode}_lower`]):i[`${this.options.view_mode}_lower`],upper_x:a.x+p[`${this.options.view_mode}_upper`],upper_y:a.upper_y,lower_x:a.x+p[`${this.options.view_mode}_lower`],lower_y:a.lower_y}}make_bars(){this.bars=this.tasks.map(t=>{const e=new C(this,t);return this.layers.bar.appendChild(e.group),e})}make_arrows(){this.arrows=[];for(let t of this.tasks){let e=[];e=t.dependencies.map(s=>{const i=this.get_task(s);if(!i)return;const o=new O(this,this.bars[i._index],this.bars[t._index]);return this.layers.arrow.appendChild(o.element),o}).filter(Boolean),this.arrows=this.arrows.concat(e)}}map_arrows_on_bars(){for(let t of this.bars)t.arrows=this.arrows.filter(e=>e.from_task.task.id===t.task.id||e.to_task.task.id===t.task.id)}set_width(){const t=this.$svg.getBoundingClientRect().width,e=this.$svg.querySelector(".grid .grid-row")?this.$svg.querySelector(".grid .grid-row").getAttribute("width"):0;t{this.unselect_all(),this.hide_popup()})}bind_bar_events(){let t=!1,e=0,s=0,i=!1,o=!1,n=null,a=[];this.bar_being_dragged=null;function p(){return t||i||o}this.$svg.onclick=d=>{d.target.classList.contains("grid-row")&&this.unselect_all()},g.on(this.$svg,"mousedown",".bar-wrapper, .handle",(d,l)=>{const u=g.closest(".bar-wrapper",l);a.forEach(w=>w.group.classList.remove("active")),l.classList.contains("left")?i=!0:l.classList.contains("right")?o=!0:l.classList.contains("bar-wrapper")&&(t=!0),u.classList.add("active"),this.popup&&this.popup.parent.classList.add("hidden"),this.popup&&this.popup.parent.classList.add("hidden"),e=d.offsetX||d.layerX,d.offsetY||d.layerY,n=u.getAttribute("data-id"),a=[n,...this.get_all_dependent_tasks(n)].map(w=>this.get_bar(w)),this.bar_being_dragged=n,a.forEach(w=>{const b=w.$bar;b.ox=b.getX(),b.oy=b.getY(),b.owidth=b.getWidth(),b.finaldx=0})}),g.on(this.$container,"scroll",d=>{let l=document.querySelectorAll(".bar-wrapper"),u=[];const f=[];let w;s&&(w=d.currentTarget.scrollLeft-s);const b=d.currentTarget.scrollLeft/this.options.column_width*this.options.step/24;let $="D MMM";["Year","Month"].includes(this.options.view_mode)?$="YYYY":["Day","Week"].includes(this.options.view_mode)?$="MMMM":this.view_is("Half Day")?$="D":this.view_is("Hour")&&($="D MMMM");let B=h.format(h.add(this.gantt_start,b,"day"),$);const v=Array.from(document.querySelectorAll(".upper-text")).find(m=>m.textContent===B);if(v&&!v.classList.contains("current-upper")){const m=document.querySelector(".current-upper");m&&(m.classList.remove("current-upper"),m.style.left=this.upper_texts_x[m.textContent]+"px",m.style.top=this.options.header_height-50+"px"),v.classList.add("current-upper");let H=this.$svg.getBoundingClientRect();v.style.left=H.x+this.$container.scrollLeft+10+"px",v.style.top=H.y+this.options.header_height-50+"px"}Array.prototype.forEach.call(l,function(m,H){f.push(m.getAttribute("data-id"))}),w&&(u=f.map(m=>this.get_bar(m)),this.options.auto_move_label&&u.forEach(m=>{m.update_label_position_on_horizontal_scroll({x:w,sx:d.currentTarget.scrollLeft})})),s=d.currentTarget.scrollLeft}),g.on(this.$svg,"mousemove",d=>{if(!p())return;const l=(d.offsetX||d.layerX)-e;a.forEach(u=>{const f=u.$bar;f.finaldx=this.get_snap_position(l),this.hide_popup(),i?n===u.task.id?u.update_bar_position({x:f.ox+f.finaldx,width:f.owidth-f.finaldx}):u.update_bar_position({x:f.ox+f.finaldx}):o?n===u.task.id&&u.update_bar_position({width:f.owidth+f.finaldx}):t&&!this.options.readonly&&!this.options.dates_readonly&&u.update_bar_position({x:f.ox+f.finaldx})})}),document.addEventListener("mouseup",d=>{t=!1,i=!1,o=!1}),g.on(this.$svg,"mouseup",d=>{this.bar_being_dragged=null,a.forEach(l=>{l.$bar.finaldx&&(l.date_changed(),l.set_action_completed())})}),this.bind_bar_progress()}bind_bar_progress(){let t=0,e=null,s=null,i=null,o=null;g.on(this.$svg,"mousedown",".handle.progress",(n,a)=>{e=!0,t=n.offsetX||n.layerX,n.offsetY||n.layerY;const d=g.closest(".bar-wrapper",a).getAttribute("data-id");s=this.get_bar(d),i=s.$bar_progress,o=s.$bar,i.finaldx=0,i.owidth=i.getWidth(),i.min_dx=-i.getWidth(),i.max_dx=o.getWidth()-i.getWidth()}),g.on(this.$svg,"mousemove",n=>{if(!e)return;let a=(n.offsetX||n.layerX)-t;a>i.max_dx&&(a=i.max_dx),a{e=!1,i&&i.finaldx&&(i.finaldx=0,s.progress_changed(),s.set_action_completed(),s=null,i=null,o=null)})}get_all_dependent_tasks(t){let e=[],s=[t];for(;s.length;){const i=s.reduce((o,n)=>(o=o.concat(this.dependency_map[n]),o),[]);e=e.concat(i),s=i.filter(o=>!s.includes(o))}return e.filter(Boolean)}get_snap_position(t){let e=t,s,i;return this.view_is(_.WEEK)?(s=t%(this.options.column_width/7),i=e-s+(s{t.classList.remove("active")}),this.popup&&this.popup.parent.classList.remove("hidden")}view_is(t){return typeof t=="string"?this.options.view_mode===t:Array.isArray(t)?t.some(e=>this.options.view_mode===e):!1}get_task(t){return this.tasks.find(e=>e.id===t)}get_bar(t){return this.bars.find(e=>e.task.id===t)}show_popup(t){this.options.popup!==!1&&(this.popup||(this.popup=new N(this.$popup_wrapper,this.options.popup)),this.popup.show(t))}hide_popup(){this.popup&&this.popup.hide()}trigger_event(t,e){this.options["on_"+t]&&this.options["on_"+t].apply(this,e)}get_oldest_starting_date(){return this.tasks.length?this.tasks.map(t=>t._start).reduce((t,e)=>e<=t?e:t):new Date}clear(){var t,e,s,i,o,n;this.$svg.innerHTML="",(e=(t=this.$header)==null?void 0:t.remove)==null||e.call(t),(i=(s=this.$current_highlight)==null?void 0:s.remove)==null||i.call(s),(n=(o=this.popup)==null?void 0:o.hide)==null||n.call(o)}}T.VIEW_MODE=_;function I(r){return r.name+"_"+Math.random().toString(36).slice(2,12)}return T}); diff --git a/index.html b/index.html index 4846135..2596b24 100644 --- a/index.html +++ b/index.html @@ -101,17 +101,25 @@ id: i, })), ]; - let gantt_chart = new Gantt('.gantt-target', tasks, { on_click(task) { console.log('Click', task); }, // on_hover (task, x, y) { // console.log("Hover", x, y); - // }, + // } view_mode: 'Day', view_mode_padding: { DAY: '3d' }, // popup_on: 'click', + custom_view_modes: [//option to add custom views + { + name: 'Custom Day', + padding: '1m', + step: 3, + unit: "day", + } + ] + // popup: false, // scroll_to: 'today', // view_mode_select: true, // dates_readonly: true, diff --git a/src/index.js b/src/index.js index b5f8053..c925fb6 100644 --- a/src/index.js +++ b/src/index.js @@ -110,11 +110,13 @@ export default class Gantt { setup_options(options) { this.options = { ...DEFAULT_OPTIONS, ...options }; - if (!options.view_mode_padding) options.view_mode_padding = {}; - for (let [key, value] of Object.entries(options.view_mode_padding)) { + const custom_mode = this.options.custom_view_modes ? this.options.custom_view_modes.find(m => m.name === this.options.view_mode) : null; + if (custom_mode) this.options = {...this.options, custom_mode} + if (!this.options.view_mode_padding) this.options.view_mode_padding = {}; + for (let [key, value] of Object.entries(this.options.view_mode_padding)) { if (typeof value === 'string') { // Configure for single value given - options.view_mode_padding[key] = [value, value]; + this.options.view_mode_padding[key] = [value, value]; } } @@ -122,6 +124,7 @@ export default class Gantt { ...VIEW_MODE_PADDING, ...options.view_mode_padding, }; + } setup_tasks(tasks) { @@ -205,7 +208,6 @@ export default class Gantt { return task; }); - this.setup_dependencies(); } @@ -234,6 +236,23 @@ export default class Gantt { update_view_scale(view_mode) { this.options.view_mode = view_mode; + const custom_mode = this.options.custom_mode + if (custom_mode) {//configure step and column width for custom view case + if (custom_mode.unit === "hour") { + this.options.step = custom_mode.step; + this.options.column_width = 38; + } else if (custom_mode.unit === "day") { + this.options.step = custom_mode.step * 24; + this.options.column_width = 38; + } else if (custom_mode.unit === "month") { + this.options.step = custom_mode.step * 24 * 30; + this.options.column_width = 120; + } + else { + this.options.step = 24; + this.options.column_width = 38; + } + } if (view_mode === VIEW_MODE.HOUR) { this.options.step = 24 / 24; this.options.column_width = 38; @@ -281,16 +300,22 @@ export default class Gantt { if (!this.gantt_end) gantt_end = new Date(); else gantt_end = date_utils.start_of(this.gantt_end, 'day'); - // add date padding on both sides - let viewKey; - for (let [key, value] of Object.entries(VIEW_MODE)) { - if (value === this.options.view_mode) { - viewKey = key; - } + const custom_mode = this.options.custom_mode + let [padding_start, padding_end] = [{duration: 1, scale: 'day'},{duration: 1, scale: 'day'}] + if (custom_mode) { + [padding_start, padding_end] = [custom_mode.padding, custom_mode.padding].map(date_utils.parse_duration) + } + else { + let viewKey; + for (let [key, value] of Object.entries(VIEW_MODE)) { + if (value === this.options.view_mode) { + viewKey = key; + } + } + [padding_start, padding_end] = this.options.view_mode_padding[ + viewKey + ].map(date_utils.parse_duration); } - const [padding_start, padding_end] = this.options.view_mode_padding[ - viewKey - ].map(date_utils.parse_duration); gantt_start = date_utils.add( gantt_start, -padding_start.duration, @@ -323,19 +348,31 @@ export default class Gantt { let cur_date = null; while (cur_date === null || cur_date < this.gantt_end) { - if (!cur_date) { - cur_date = date_utils.clone(this.gantt_start); - } else { - if (this.view_is(VIEW_MODE.YEAR)) { - cur_date = date_utils.add(cur_date, 1, 'year'); - } else if (this.view_is(VIEW_MODE.MONTH)) { - cur_date = date_utils.add(cur_date, 1, 'month'); + if (this.options.custom_mode) { + const step = this.options.custom_mode.step || 1; + const unit = this.options.custom_mode.unit || 'day'; + + if (!cur_date) { + cur_date = date_utils.clone(this.gantt_start); } else { - cur_date = date_utils.add( - cur_date, - this.options.step, - 'hour', - ); + cur_date = date_utils.add(cur_date, step, unit); + } + } + else { + if (!cur_date) { + cur_date = date_utils.clone(this.gantt_start); + } else { + if (this.view_is(VIEW_MODE.YEAR)) { + cur_date = date_utils.add(cur_date, 1, 'year'); + } else if (this.view_is(VIEW_MODE.MONTH)) { + cur_date = date_utils.add(cur_date, 1, 'month'); + } else { + cur_date = date_utils.add( + cur_date, + this.options.step, + 'hour', + ); + } } } this.dates.push(cur_date); @@ -392,7 +429,6 @@ export default class Gantt { this.options.padding + (this.options.bar_height + this.options.padding) * this.tasks.length; - createSVG('rect', { x: 0, y: 0, @@ -410,12 +446,11 @@ export default class Gantt { make_grid_rows() { const rows_layer = createSVG('g', { append_to: this.layers.grid }); - + const row_width = this.dates.length * this.options.column_width; const row_height = this.options.bar_height + this.options.padding; let row_y = this.options.header_height + this.options.padding / 2; - for (let _ of this.tasks) { createSVG('rect', { x: 0, @@ -448,7 +483,7 @@ export default class Gantt { $upper_header.classList.add('upper-header'); this.$upper_header = $upper_header; this.$header.appendChild($upper_header); - + let $lower_header = document.createElement('div'); $lower_header.classList.add('lower-header'); this.$lower_header = $lower_header; @@ -596,7 +631,6 @@ export default class Gantt { if (this.view_is(VIEW_MODE.MONTH) && date.getMonth() % 3 === 0) { tick_class += ' thick'; } - createSVG('path', { d: `M ${tick_x} ${tick_y} v ${tick_height}`, class: tick_class, @@ -744,6 +778,7 @@ export default class Gantt { classes: 'lower-text', append_to: this.$lower_header, }); + $lower_text.innerText = date.lower_text; $lower_text.style.left = +$lower_text.style.left.slice(0, -2) + 'px'; @@ -776,70 +811,104 @@ export default class Gantt { } get_date_info(date, last_date_info) { + let last_date = last_date_info ? last_date_info.date : date_utils.add(date, 1, 'day'); - const date_text = { - Hour_lower: date_utils.format(date, 'HH', this.options.language), - 'Quarter Day_lower': date_utils.format( - date, - 'HH', - this.options.language, - ), - 'Half Day_lower': date_utils.format( - date, - 'HH', - this.options.language, - ), - Day_lower: - date.getDate() !== last_date.getDate() + let date_text = {} + const custom_mode = this.options.custom_mode + if (custom_mode) { + let lower_text,upper_text + const unit = custom_mode ? custom_mode.unit.toLowerCase() : 'day'; + if (unit === 'hour') { + lower_text = date_utils.format(date, 'HH', this.options.language); + upper_text = date.getDate() !== last_date.getDate() + ? date_utils.format(date, 'D MMMM', this.options.language) + : ''; + } else if (unit === 'day') { + lower_text = date.getDate() !== last_date.getDate() ? date_utils.format(date, 'D', this.options.language) - : '', - Week_lower: - date.getMonth() !== last_date.getMonth() - ? date_utils.format(date, 'D MMM', this.options.language) - : date_utils.format(date, 'D', this.options.language), - Month_lower: date_utils.format(date, 'MMMM', this.options.language), - Year_lower: date_utils.format(date, 'YYYY', this.options.language), - Hour_upper: - date.getDate() !== last_date.getDate() - ? date_utils.format(date, 'D MMMM', this.options.language) - : '', - 'Quarter Day_upper': - date.getDate() !== last_date.getDate() - ? date_utils.format(date, 'D MMM', this.options.language) - : '', - 'Half Day_upper': - date.getDate() !== last_date.getDate() - ? date.getMonth() !== last_date.getMonth() - ? date_utils.format( - date, - 'D MMM', - this.options.language, - ) - : date_utils.format(date, 'D', this.options.language) - : '', - Day_upper: - date.getMonth() !== last_date.getMonth() || !last_date_info + : ''; + upper_text = date.getMonth() !== last_date.getMonth() || !last_date_info ? date_utils.format(date, 'MMMM', this.options.language) - : '', - Week_upper: - date.getMonth() !== last_date.getMonth() - ? date_utils.format(date, 'MMMM', this.options.language) - : '', - Month_upper: - date.getFullYear() !== last_date.getFullYear() + : ''; + } else if (unit === 'month') { + lower_text = date_utils.format(date, 'MMMM', this.options.language); + upper_text = date.getFullYear() !== last_date.getFullYear() ? date_utils.format(date, 'YYYY', this.options.language) - : '', - Year_upper: - date.getFullYear() !== last_date.getFullYear() - ? date_utils.format(date, 'YYYY', this.options.language) - : '', - }; - let column_width = this.view_is(VIEW_MODE.MONTH) + : ''; + } else { + lower_text = date_utils.format(date, 'YYYY', this.options.language); + upper_text = ''; // Default to no upper text for very large units + } + date_text[`${custom_mode.name}_upper`] = upper_text + date_text[`${custom_mode.name}_lower`] = lower_text + } + else { + date_text = { + Hour_lower: date_utils.format(date, 'HH', this.options.language), + 'Quarter Day_lower': date_utils.format( + date, + 'HH', + this.options.language, + ), + 'Half Day_lower': date_utils.format( + date, + 'HH', + this.options.language, + ), + Day_lower: + date.getDate() !== last_date.getDate() + ? date_utils.format(date, 'D', this.options.language) + : '', + Week_lower: + date.getMonth() !== last_date.getMonth() + ? date_utils.format(date, 'D MMM', this.options.language) + : date_utils.format(date, 'D', this.options.language), + Month_lower: date_utils.format(date, 'MMMM', this.options.language), + Year_lower: date_utils.format(date, 'YYYY', this.options.language), + Hour_upper: + date.getDate() !== last_date.getDate() + ? date_utils.format(date, 'D MMMM', this.options.language) + : '', + 'Quarter Day_upper': + date.getDate() !== last_date.getDate() + ? date_utils.format(date, 'D MMM', this.options.language) + : '', + 'Half Day_upper': + date.getDate() !== last_date.getDate() + ? date.getMonth() !== last_date.getMonth() + ? date_utils.format( + date, + 'D MMM', + this.options.language, + ) + : date_utils.format(date, 'D', this.options.language) + : '', + Day_upper: + date.getMonth() !== last_date.getMonth() || !last_date_info + ? date_utils.format(date, 'MMMM', this.options.language) + : '', + Week_upper: + date.getMonth() !== last_date.getMonth() + ? date_utils.format(date, 'MMMM', this.options.language) + : '', + Month_upper: + date.getFullYear() !== last_date.getFullYear() + ? date_utils.format(date, 'YYYY', this.options.language) + : '', + Year_upper: + date.getFullYear() !== last_date.getFullYear() + ? date_utils.format(date, 'YYYY', this.options.language) + : '', + }; + } + + let column_width = custom_mode && custom_mode.lower_text && custom_mode.lower_text.column_width ? custom_mode.lower_text.column_width * (custom_mode.step ?? 1) : this.view_is(VIEW_MODE.MONTH) ? (date_utils.get_days_in_month(date) * this.options.column_width) / 30 : this.options.column_width; + const base_pos = { x: last_date_info ? last_date_info.base_pos_x + last_date_info.column_width @@ -863,6 +932,10 @@ export default class Gantt { Year_lower: column_width / 2, Year_upper: (column_width * 30) / 2, }; + if (custom_mode){ + x_pos[`${custom_mode.name}_upper`] = column_width / 2 + x_pos[`${custom_mode.name}_lower`] = column_width / (custom_mode.unit.toLowerCase() === 'day' ? 1 : 2) + } return { date, formatted_date: date_utils.format(date).replaceAll(' ', '_'),