@workiom/frappe-gantt 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -1
- package/dist/frappe-gantt.css +1 -1
- package/dist/frappe-gantt.es.js +398 -313
- package/dist/frappe-gantt.umd.js +11 -11
- package/package.json +1 -1
- package/src/bar.js +184 -0
- package/src/defaults.js +1 -0
- package/src/index.js +32 -4
- package/src/styles/gantt.css +31 -0
package/dist/frappe-gantt.umd.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
(function(x
|
|
1
|
+
(function(x,v){typeof exports=="object"&&typeof module<"u"?module.exports=v():typeof define=="function"&&define.amd?define(v):(x=typeof globalThis<"u"?globalThis:x||self,x.Gantt=v())})(this,function(){"use strict";const x="year",v="month",L="day",T="hour",Y="minute",A="second",H="millisecond",c={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 i,s;const n=r.split(" ");i=n[0].split(t).map(a=>parseInt(a,10)),s=n[1]&&n[1].split(e),i[1]=i[1]?i[1]-1:0;let o=i;return s&&s.length&&(s.length===4&&(s[3]="0."+s[3],s[3]=parseFloat(s[3])*1e3),o=o.concat(s)),new Date(...o)}},to_string(r,t=!1){if(!(r instanceof Date))throw new TypeError("Invalid argument type");const e=this.get_date_values(r).map((n,o)=>(o===1&&(n=n+1),o===6?M(n+"",3,"0"):M(n+"",2,"0"))),i=`${e[0]}-${e[1]}-${e[2]}`,s=`${e[3]}:${e[4]}:${e[5]}.${e[6]}`;return i+(t?" "+s:"")},format(r,t="YYYY-MM-DD HH:mm:ss.SSS",e="en"){const i=new Intl.DateTimeFormat(e,{month:"long"}),s=new Intl.DateTimeFormat(e,{month:"short"}),n=i.format(r),o=n.charAt(0).toUpperCase()+n.slice(1),a=this.get_date_values(r).map(l=>M(l,2,0)),h={YYYY:a[0],MM:M(+a[1]+1,2,0),DD:a[2],HH:a[3],mm:a[4],ss:a[5],SSS:a[6],D:a[2],MMMM:o,MMM:s.format(r)};let d=t;const _=[];return Object.keys(h).sort((l,p)=>p.length-l.length).forEach(l=>{d.includes(l)&&(d=d.replaceAll(l,`$${_.length}`),_.push(h[l]))}),_.forEach((l,p)=>{d=d.replaceAll(`$${p}`,l)}),d},diff(r,t,e="day"){let i,s,n,o,a,h,d;i=r-t+(t.getTimezoneOffset()-r.getTimezoneOffset())*6e4,s=i/1e3,o=s/60,n=o/60,a=n/24;let _=r.getFullYear()-t.getFullYear(),l=r.getMonth()-t.getMonth();return l+=a%30/30,h=_*12+l,r.getDate()<t.getDate()&&h--,d=h/12,e.endsWith("s")||(e+="s"),Math.round({milliseconds:i,seconds:s,minutes:o,hours:n,days:a,months:h,years:d}[e]*100)/100},today(){const r=this.get_date_values(new Date).slice(0,3);return new Date(...r)},now(){return new Date},add(r,t,e){t=parseInt(t,10);const i=[r.getFullYear()+(e===x?t:0),r.getMonth()+(e===v?t:0),r.getDate()+(e===L?t:0),r.getHours()+(e===T?t:0),r.getMinutes()+(e===Y?t:0),r.getSeconds()+(e===A?t:0),r.getMilliseconds()+(e===H?t:0)];return new Date(...i)},start_of(r,t){const e={[x]:6,[v]:5,[L]:4,[T]:3,[Y]:2,[A]:1,[H]:0};function i(n){const o=e[t];return e[n]<=o}const s=[r.getFullYear(),i(x)?0:r.getMonth(),i(v)?1:r.getDate(),i(L)?0:r.getHours(),i(T)?0:r.getMinutes(),i(Y)?0:r.getSeconds(),i(A)?0:r.getMilliseconds()];return new Date(...s)},clone(r){return new Date(...this.get_date_values(r))},get_date_values(r){return[r.getFullYear(),r.getMonth(),r.getDate(),r.getHours(),r.getMinutes(),r.getSeconds(),r.getMilliseconds()]},convert_scales(r,t){const e={millisecond:11574074074074074e-24,second:11574074074074073e-21,minute:.0006944444444444445,hour:.041666666666666664,day:1,month:30,year:365},{duration:i,scale:s}=this.parse_duration(r);return i*e[s]/e[t]},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 i=r.getFullYear();return i%4===0&&i%100!=0||i%400===0?29:28},get_days_in_year(r){return r.getFullYear()%4?365:366}};function M(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 u(r,t){return typeof r=="string"?(t||document).querySelector(r):r||null}function f(r,t){const e=document.createElementNS("http://www.w3.org/2000/svg",r);for(let i in t)i==="append_to"?t.append_to.appendChild(e):i==="innerHTML"?e.innerHTML=t.innerHTML:i==="clipPath"?e.setAttribute("clip-path","url(#"+t[i]+")"):e.setAttribute(i,t[i]);return e}function E(r,t,e,i){const s=z(r,t,e,i);if(s===r){const n=document.createEvent("HTMLEvents");n.initEvent("click",!0,!0),n.eventName="click",s.dispatchEvent(n)}}function z(r,t,e,i,s="0.4s",n="0.1s"){const o=r.querySelector("animate");if(o)return u.attr(o,{attributeName:t,from:e,to:i,dur:s,begin:"click + "+n}),r;const a=f("animate",{attributeName:t,from:e,to:i,dur:s,begin:n,calcMode:"spline",values:e+";"+i,keyTimes:"0; 1",keySplines:W("ease-out")});return r.appendChild(a),r}function W(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]}u.on=(r,t,e,i)=>{i?u.delegate(r,t,e,i):(i=e,u.bind(r,t,i))},u.off=(r,t,e)=>{r.removeEventListener(t,e)},u.bind=(r,t,e)=>{t.split(/\s+/).forEach(function(i){r.addEventListener(i,e)})},u.delegate=(r,t,e,i)=>{r.addEventListener(t,function(s){const n=s.target.closest(e);n&&(s.delegatedTarget=n,i.call(this,s,n))})},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 r.getAttribute(t);if(typeof t=="object"){for(let i in t)u.attr(r,i,t[i]);return}r.setAttribute(t,e)};class C{constructor(t,e,i){this.gantt=t,this.from_task=e,this.to_task=i,this.is_critical=this.check_critical_path(),this.calculate_path(),this.draw()}check_critical_path(){return this.gantt.options.critical_path?this.from_task.task._is_critical===!0&&this.to_task.task._is_critical===!0:!1}calculate_path(){let t=this.from_task.$bar.getX()+this.from_task.$bar.getWidth()/2;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;t-=10;let i=this.gantt.config.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/2,s=this.to_task.$bar.getX()-13,n=this.gantt.config.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/2;const o=this.from_task.task._index>this.to_task.task._index;let a=this.gantt.options.arrow_curve;const h=o?1:0;let d=o?-a:a;if(this.to_task.$bar.getX()<=this.from_task.$bar.getX()+this.gantt.options.padding){let _=this.gantt.options.padding/2-a;_<0&&(_=0,a=this.gantt.options.padding/2,d=o?-a:a);const l=this.to_task.$bar.getY()+this.to_task.$bar.getHeight()/2-d,p=this.to_task.$bar.getX()-this.gantt.options.padding;this.path=`
|
|
2
2
|
M ${t} ${i}
|
|
3
3
|
v ${_}
|
|
4
|
-
a ${
|
|
5
|
-
H ${
|
|
6
|
-
a ${
|
|
7
|
-
V ${
|
|
8
|
-
a ${
|
|
4
|
+
a ${a} ${a} 0 0 1 ${-a} ${a}
|
|
5
|
+
H ${p}
|
|
6
|
+
a ${a} ${a} 0 0 ${h} ${-a} ${d}
|
|
7
|
+
V ${l}
|
|
8
|
+
a ${a} ${a} 0 0 ${h} ${a} ${d}
|
|
9
9
|
L ${s} ${n}
|
|
10
10
|
m -5 -5
|
|
11
11
|
l 5 5
|
|
12
|
-
l -5 5`}else{s<t+
|
|
12
|
+
l -5 5`}else{s<t+a&&(a=s-t);let _=o?n+a:n-a;this.path=`
|
|
13
13
|
M ${t} ${i}
|
|
14
14
|
V ${_}
|
|
15
|
-
a ${
|
|
15
|
+
a ${a} ${a} 0 0 ${h} ${a} ${a}
|
|
16
16
|
L ${s} ${n}
|
|
17
17
|
m -5 -5
|
|
18
18
|
l 5 5
|
|
19
|
-
l -5 5`}}draw(){this.element=f("path",{d:this.path,"data-from":this.from_task.task.id,"data-to":this.to_task.task.id,class:this.is_critical?"arrow-critical":""})}update(){this.calculate_path(),this.element.setAttribute("d",this.path)}}class F{constructor(t,e){this.set_defaults(t,e),this.prepare_wrappers(),this.prepare_helpers(),this.refresh()}refresh(){this.bar_group.innerHTML="",this.handle_group.innerHTML="",this.task.custom_class?this.group.classList.add(this.task.custom_class):this.group.classList=["bar-wrapper"],this.prepare_values(),this.draw(),this.bind()}set_defaults(t,e){this.action_completed=!1,this.gantt=t,this.task=e,this.name=this.name||""}prepare_wrappers(){this.group=f("g",{class:"bar-wrapper"+(this.task.custom_class?" "+this.task.custom_class:""),"data-id":this.task.id}),this.bar_group=f("g",{class:"bar-group",append_to:this.group}),this.handle_group=f("g",{class:"handle-group",append_to:this.group})}prepare_values(){this.invalid=this.task.invalid,this.height=this.gantt.options.bar_height,this.image_size=this.height-5,(!this.task._start||!(this.task._start instanceof Date))&&(this.task._start=new Date(this.task.start)),(!this.task._end||!(this.task._end instanceof Date))&&(this.task._end=new Date(this.task.end)),this.compute_x(),this.compute_y(),this.compute_duration(),this.corner_radius=this.gantt.options.bar_corner_radius,this.width=this.gantt.config.column_width*this.duration,(!this.task.progress||this.task.progress<0)&&(this.task.progress=0),this.task.progress>100&&(this.task.progress=100)}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=f("rect",{x:this.x,y:this.y,width:this.width,height:this.height,rx:this.corner_radius,ry:this.corner_radius,class:"bar",append_to:this.bar_group}),this.task.color&&(this.$bar.style.fill=this.task.color),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=f("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.progress_width=this.calculate_progress_width();let t=this.corner_radius;/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(t=this.corner_radius+2),this.$bar_progress=f("rect",{x:this.x,y:this.y,width:this.progress_width,height:this.height,rx:t,ry:t,class:"bar-progress",append_to:this.bar_group}),this.task.color_progress&&(this.$bar_progress.style.fill=this.task.color_progress);const e=l.diff(this.task._start,this.gantt.gantt_start,this.gantt.config.unit)/this.gantt.config.step*this.gantt.config.column_width;let i=this.gantt.create_el({classes:`date-range-highlight hide highlight-${this.task.id}`,width:this.width,left:e});this.$date_highlight=i,this.gantt.$lower_header.prepend(this.$date_highlight),A(this.$bar_progress,"width",0,this.progress_width)}calculate_progress_width(){const t=this.$bar.getWidth(),e=this.x+t,i=this.gantt.config.ignored_positions.reduce((h,d)=>h+(d>=this.x&&d<e),0)*this.gantt.config.column_width;let s=(t-i)*this.task.progress/100;const n=this.x+s,a=this.gantt.config.ignored_positions.reduce((h,d)=>h+(d>=this.x&&d<n),0)*this.gantt.config.column_width;s+=a;let o=this.gantt.get_ignored_region(this.x+s);for(;o.length;)s+=this.gantt.config.column_width,o=this.gantt.get_ignored_region(this.x+s);return this.progress_width=s,s}draw_label(){let t=this.x+this.$bar.getWidth()/2;this.task.thumbnail&&(t=this.x+this.image_size+5),f("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,i,s;i=f("defs",{append_to:this.bar_group}),f("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:i}),s=f("clipPath",{id:"clip_"+this.task.id,append_to:i}),f("use",{href:"#rect_"+this.task.id,append_to:s}),f("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=3;if(this.handles=[],this.gantt.options.readonly_dates||(this.handles.push(f("rect",{x:t.getEndX()-e/2,y:t.getY()+this.height/4,width:e,height:this.height/2,rx:2,ry:2,class:"handle right",append_to:this.handle_group})),this.handles.push(f("rect",{x:t.getX()-e/2,y:t.getY()+this.height/4,width:e,height:this.height/2,rx:2,ry:2,class:"handle left",append_to:this.handle_group}))),!this.gantt.options.readonly_progress){const i=this.$bar_progress;this.$handle_progress=f("circle",{cx:i.getEndX(),cy:i.getY()+i.getHeight()/2,r:4.5,class:"handle progress",append_to:this.handle_group}),this.handles.push(this.$handle_progress)}for(let i of this.handles)p.on(i,"mouseenter",()=>i.classList.add("active")),p.on(i,"mouseleave",()=>i.classList.remove("active"))}bind(){this.invalid||this.setup_click_event()}setup_click_event(){let t=this.task.id;p.on(this.group,"mouseover",s=>{this.gantt.trigger_event("hover",[this.task,s.screenX,s.screenY,s])}),this.gantt.options.popup_on==="click"&&p.on(this.group,"mouseup",s=>{const n=s.offsetX||s.layerX;if(this.$handle_progress){const a=+this.$handle_progress.getAttribute("cx");if(a>n-1&&a<n+1||this.gantt.bar_being_dragged)return}this.gantt.show_popup({x:s.offsetX||s.layerX,y:s.offsetY||s.layerY,task:this.task,target:this.$bar})});let e;p.on(this.group,"mouseenter",s=>{e=setTimeout(()=>{this.gantt.options.popup_on==="hover"&&this.gantt.show_popup({x:s.offsetX||s.layerX,y:s.offsetY||s.layerY,task:this.task,target:this.$bar}),this.gantt.$container.querySelector(`.highlight-${t}`).classList.remove("hide")},200)}),p.on(this.group,"mouseleave",()=>{var s,n;clearTimeout(e),this.gantt.options.popup_on==="hover"&&((n=(s=this.gantt.popup)==null?void 0:s.hide)==null||n.call(s)),this.gantt.$container.querySelector(`.highlight-${t}`).classList.add("hide")}),p.on(this.group,"click",()=>{this.gantt.trigger_event("click",[this.task])}),p.on(this.group,"dblclick",s=>{this.action_completed||(this.group.classList.remove("active"),this.gantt.popup&&this.gantt.popup.parent.classList.remove("hide"),this.gantt.trigger_event("double_click",[this.task]))});let i=!1;p.on(this.group,"touchstart",s=>{if(!i)return i=!0,setTimeout(function(){i=!1},300),!1;s.preventDefault(),!this.action_completed&&(this.group.classList.remove("active"),this.gantt.popup&&this.gantt.popup.parent.classList.remove("hide"),this.gantt.trigger_event("double_click",[this.task]))})}update_bar_position({x:t=null,width:e=null}){const i=this.$bar;if(t){if(!this.validate_dependency_constraints(t,e))return;this.update_attr(i,"x",t),this.x=t,this.$date_highlight.style.left=t+"px"}e>0&&(this.update_attr(i,"width",e),this.$date_highlight.style.width=e+"px"),this.update_label_position(),this.update_handle_position(),this.date_changed(),this.compute_duration(),this.gantt.options.show_expected_progress&&this.update_expected_progressbar_position(),this.update_progressbar_position(),this.update_arrow_position()}validate_dependency_constraints(t,e=null){const i=this.task.dependencies_type||this.gantt.options.dependencies_type;if(i==="fixed")return this.task.dependencies.map(_=>this.gantt.get_bar(_).$bar.getX()).reduce((_,c)=>_&&t>=c,!0);const s=t/this.gantt.config.column_width,n=l.add(this.gantt.gantt_start,s*this.gantt.config.step,this.gantt.config.unit),o=(e||this.$bar.getWidth())/this.gantt.config.column_width,h=l.add(n,o*this.gantt.config.step,this.gantt.config.unit);for(const d of this.task.dependencies){const _=this.gantt.get_bar(d);if(!_)continue;const c=_.task;switch(i){case"finish-to-start":if(n<c._end)return!1;break;case"start-to-start":if(n<c._start)return!1;break;case"finish-to-finish":if(h<c._end)return!1;break;case"start-to-finish":if(h<c._start)return!1;break}}return!0}update_label_position_on_horizontal_scroll({x:t,sx:e}){const i=this.gantt.$container,s=this.group.querySelector(".bar-label"),n=this.group.querySelector(".bar-img")||"",a=this.bar_group.querySelector(".img_mask")||"";let o=this.$bar.getX()+this.$bar.getWidth(),h=s.getX()+t,d=n&&n.getX()+t||0,_=n&&n.getBBox().width+7||7,c=h+s.getBBox().width+7,g=e+i.clientWidth/2;s.classList.contains("big")||(c<o&&t>0&&c<g||h-_>this.$bar.getX()&&t<0&&c>g)&&(s.setAttribute("x",h),n&&(n.setAttribute("x",d),a.setAttribute("x",d)))}date_changed(){let t=!1;const{new_start_date:e,new_end_date:i}=this.compute_start_end_date();Number(this.task._start)!==Number(e)&&(t=!0,this.task._start=e),Number(this.task._end)!==Number(i)&&(t=!0,this.task._end=i),t&&this.gantt.trigger_event("date_change",[this.task,e,l.add(i,-1,"second")])}progress_changed(){this.task.progress=this.compute_progress(),this.gantt.trigger_event("progress_change",[this.task,this.task.progress])}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.config.column_width;let i=l.add(this.gantt.gantt_start,e*this.gantt.config.step,this.gantt.config.unit);const s=t.getWidth()/this.gantt.config.column_width,n=l.add(i,s*this.gantt.config.step,this.gantt.config.unit);return{new_start_date:i,new_end_date:n}}compute_progress(){this.progress_width=this.$bar_progress.getWidth(),this.x=this.$bar_progress.getBBox().x;const t=this.x+this.progress_width,e=this.progress_width-this.gantt.config.ignored_positions.reduce((s,n)=>s+(n>=this.x&&n<=t),0)*this.gantt.config.column_width;if(e<0)return 0;const i=this.$bar.getWidth()-this.ignored_duration_raw*this.gantt.config.column_width;return parseInt(e/i*100,10)}compute_expected_progress(){this.expected_progress=l.diff(l.today(),this.task._start,"hour")/this.gantt.config.step,this.expected_progress=(this.expected_progress<this.duration?this.expected_progress:this.duration)*100/this.duration}compute_x(){const{column_width:t}=this.gantt.config,e=this.task._start,i=this.gantt.gantt_start;let n=l.diff(e,i,this.gantt.config.unit)/this.gantt.config.step*t;this.x=n}compute_y(){this.y=this.gantt.config.header_height+this.gantt.options.padding/2+this.task._index*(this.height+this.gantt.options.padding)}compute_duration(){let t=0,e=0;for(let i=new Date(this.task._start);i<this.task._end;i.setDate(i.getDate()+1))e++,!this.gantt.config.ignored_dates.find(s=>s.getTime()===i.getTime())&&(!this.gantt.config.ignored_function||!this.gantt.config.ignored_function(i))&&t++;this.task.actual_duration=t,this.task.ignored_duration=e-t,this.duration=l.convert_scales(e+"d",this.gantt.config.unit)/this.gantt.config.step,this.actual_duration_raw=l.convert_scales(t+"d",this.gantt.config.unit)/this.gantt.config.step,this.ignored_duration_raw=this.duration-this.actual_duration_raw}update_attr(t,e,i){return i=+i,isNaN(i)||t.setAttribute(e,i),t}update_expected_progressbar_position(){this.invalid||(this.$expected_bar_progress.setAttribute("x",this.$bar.getX()),this.compute_expected_progress(),this.$expected_bar_progress.setAttribute("width",this.gantt.config.column_width*this.actual_duration_raw*(this.expected_progress/100)||0))}update_progressbar_position(){this.invalid||this.gantt.options.readonly||(this.$bar_progress.setAttribute("x",this.$bar.getX()),this.$bar_progress.setAttribute("width",this.calculate_progress_width()))}update_label_position(){const t=this.bar_group.querySelector(".img_mask")||"",e=this.$bar,i=this.group.querySelector(".bar-label"),s=this.group.querySelector(".bar-img");let n=5,a=this.image_size+10;const o=i.getBBox().width,h=e.getWidth();o>h?(i.classList.add("big"),s?(s.setAttribute("x",e.getEndX()+n),t.setAttribute("x",e.getEndX()+n),i.setAttribute("x",e.getEndX()+a)):i.setAttribute("x",e.getEndX()+n)):(i.classList.remove("big"),s?(s.setAttribute("x",e.getX()+n),t.setAttribute("x",e.getX()+n),i.setAttribute("x",e.getX()+h/2+a)):i.setAttribute("x",e.getX()+h/2-o/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()),this.handle_group.querySelector(".handle.right").setAttribute("x",t.getEndX());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,i){this.parent=t,this.popup_func=e,this.gantt=i,this.make()}make(){this.parent.innerHTML=`
|
|
19
|
+
l -5 5`}}draw(){this.element=f("path",{d:this.path,"data-from":this.from_task.task.id,"data-to":this.to_task.task.id,class:this.is_critical?"arrow-critical":""})}update(){this.calculate_path(),this.element.setAttribute("d",this.path)}}class q{constructor(t,e){this.set_defaults(t,e),this.prepare_wrappers(),this.prepare_helpers(),this.refresh()}refresh(){this.bar_group.innerHTML="",this.handle_group.innerHTML="",this.task.custom_class?this.group.classList.add(this.task.custom_class):this.group.classList=["bar-wrapper"],this.prepare_values(),this.draw(),this.bind()}set_defaults(t,e){this.action_completed=!1,this.gantt=t,this.task=e,this.name=this.name||"",this.is_dragging=!1,this.is_hovering_bar=!1,this.is_hovering_icon=!1,this.add_icon_hide_timeout=null}prepare_wrappers(){this.group=f("g",{class:"bar-wrapper"+(this.task.custom_class?" "+this.task.custom_class:""),"data-id":this.task.id}),this.bar_group=f("g",{class:"bar-group",append_to:this.group}),this.handle_group=f("g",{class:"handle-group",append_to:this.group})}prepare_values(){this.invalid=this.task.invalid,this.height=this.gantt.options.bar_height,this.image_size=this.height-5,(!this.task._start||!(this.task._start instanceof Date))&&(this.task._start=new Date(this.task.start)),(!this.task._end||!(this.task._end instanceof Date))&&(this.task._end=new Date(this.task.end)),this.compute_x(),this.compute_y(),this.compute_duration(),this.corner_radius=this.gantt.options.bar_corner_radius,this.width=this.gantt.config.column_width*this.duration,(!this.task.progress||this.task.progress<0)&&(this.task.progress=0),this.task.progress>100&&(this.task.progress=100)}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(),this.gantt.options.task_add_icon_position&&this.draw_add_task_icon()}draw_bar(){this.$bar=f("rect",{x:this.x,y:this.y,width:this.width,height:this.height,rx:this.corner_radius,ry:this.corner_radius,class:"bar",append_to:this.bar_group}),this.task.color&&(this.$bar.style.fill=this.task.color),E(this.$bar,"width",0,this.width),this.invalid&&this.$bar.classList.add("bar-invalid")}draw_expected_progress_bar(){this.invalid||(this.$expected_bar_progress=f("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}),E(this.$expected_bar_progress,"width",0,this.expected_progress_width))}draw_progress_bar(){if(this.invalid)return;this.progress_width=this.calculate_progress_width();let t=this.corner_radius;/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(t=this.corner_radius+2),this.$bar_progress=f("rect",{x:this.x,y:this.y,width:this.progress_width,height:this.height,rx:t,ry:t,class:"bar-progress",append_to:this.bar_group}),this.task.color_progress&&(this.$bar_progress.style.fill=this.task.color_progress);const e=c.diff(this.task._start,this.gantt.gantt_start,this.gantt.config.unit)/this.gantt.config.step*this.gantt.config.column_width;let i=this.gantt.create_el({classes:`date-range-highlight hide highlight-${this.task.id}`,width:this.width,left:e});this.$date_highlight=i,this.gantt.$lower_header.prepend(this.$date_highlight),E(this.$bar_progress,"width",0,this.progress_width)}calculate_progress_width(){const t=this.$bar.getWidth(),e=this.x+t,i=this.gantt.config.ignored_positions.reduce((h,d)=>h+(d>=this.x&&d<e),0)*this.gantt.config.column_width;let s=(t-i)*this.task.progress/100;const n=this.x+s,o=this.gantt.config.ignored_positions.reduce((h,d)=>h+(d>=this.x&&d<n),0)*this.gantt.config.column_width;s+=o;let a=this.gantt.get_ignored_region(this.x+s);for(;a.length;)s+=this.gantt.config.column_width,a=this.gantt.get_ignored_region(this.x+s);return this.progress_width=s,s}draw_label(){let t=this.x+this.$bar.getWidth()/2;this.task.thumbnail&&(t=this.x+this.image_size+5),f("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,i,s;i=f("defs",{append_to:this.bar_group}),f("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:i}),s=f("clipPath",{id:"clip_"+this.task.id,append_to:i}),f("use",{href:"#rect_"+this.task.id,append_to:s}),f("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_add_task_icon(){this.icon_size=20,this.icon_padding=5;let i;if(this.gantt.options.task_add_icon_position==="before")i=this.x-20-5;else if(this.gantt.options.task_add_icon_position==="after")i=this.x+this.$bar.getWidth()+5;else return;const s=this.y+(this.height-20)/2;this.$add_icon_group=f("g",{class:"add-task-icon hide",append_to:this.handle_group}),this.$add_icon_circle=f("circle",{cx:i+20/2,cy:s+20/2,r:20/2,class:"add-task-icon-bg",append_to:this.$add_icon_group}),this.$add_icon_vertical=f("line",{x1:i+20/2,y1:s+5,x2:i+20/2,y2:s+20-5,class:"add-task-icon-plus",append_to:this.$add_icon_group}),this.$add_icon_horizontal=f("line",{x1:i+5,y1:s+20/2,x2:i+20-5,y2:s+20/2,class:"add-task-icon-plus",append_to:this.$add_icon_group}),u.on(this.$add_icon_group,"mousedown",n=>{n.stopPropagation()}),u.on(this.$add_icon_group,"mouseup",n=>{n.stopPropagation()}),u.on(this.$add_icon_group,"click",n=>{n.stopPropagation(),this.gantt.trigger_event("task_add",[this.task])}),u.on(this.$add_icon_group,"mouseenter",n=>{this.is_hovering_icon=!0,this.add_icon_hide_timeout&&(clearTimeout(this.add_icon_hide_timeout),this.add_icon_hide_timeout=null),this.$add_icon_group.classList.add("active"),this.$add_icon_group.classList.remove("hide"),n.stopPropagation()}),u.on(this.$add_icon_group,"mouseleave",()=>{this.is_hovering_icon=!1,this.$add_icon_group.classList.remove("active"),this.is_hovering_bar||this.$add_icon_group.classList.add("hide")})}draw_resize_handles(){if(this.invalid||this.gantt.options.readonly)return;const t=this.$bar,e=3;if(this.handles=[],this.gantt.options.readonly_dates||(this.handles.push(f("rect",{x:t.getEndX()-e/2,y:t.getY()+this.height/4,width:e,height:this.height/2,rx:2,ry:2,class:"handle right",append_to:this.handle_group})),this.handles.push(f("rect",{x:t.getX()-e/2,y:t.getY()+this.height/4,width:e,height:this.height/2,rx:2,ry:2,class:"handle left",append_to:this.handle_group}))),!this.gantt.options.readonly_progress){const i=this.$bar_progress;this.$handle_progress=f("circle",{cx:i.getEndX(),cy:i.getY()+i.getHeight()/2,r:4.5,class:"handle progress",append_to:this.handle_group}),this.handles.push(this.$handle_progress)}for(let i of this.handles)u.on(i,"mouseenter",()=>i.classList.add("active")),u.on(i,"mouseleave",()=>i.classList.remove("active"))}bind(){this.invalid||this.setup_click_event()}setup_click_event(){let t=this.task.id;u.on(this.group,"mouseover",s=>{this.gantt.trigger_event("hover",[this.task,s.screenX,s.screenY,s])}),this.gantt.options.popup_on==="click"&&u.on(this.group,"mouseup",s=>{const n=s.offsetX||s.layerX;if(this.$handle_progress){const o=+this.$handle_progress.getAttribute("cx");if(o>n-1&&o<n+1||this.gantt.bar_being_dragged)return}this.gantt.show_popup({x:s.offsetX||s.layerX,y:s.offsetY||s.layerY,task:this.task,target:this.$bar})});let e;u.on(this.group,"mouseenter",s=>{e=setTimeout(()=>{this.gantt.options.popup_on==="hover"&&this.gantt.show_popup({x:s.offsetX||s.layerX,y:s.offsetY||s.layerY,task:this.task,target:this.$bar}),this.gantt.$container.querySelector(`.highlight-${t}`).classList.remove("hide")},200),this.$add_icon_group&&(this.is_hovering_bar=!0,this.add_icon_hide_timeout&&(clearTimeout(this.add_icon_hide_timeout),this.add_icon_hide_timeout=null),this.is_dragging||this.$add_icon_group.classList.remove("hide"))}),u.on(this.group,"mouseleave",()=>{var s,n;clearTimeout(e),this.gantt.options.popup_on==="hover"&&((n=(s=this.gantt.popup)==null?void 0:s.hide)==null||n.call(s)),this.gantt.$container.querySelector(`.highlight-${t}`).classList.add("hide"),this.$add_icon_group&&(this.is_hovering_bar=!1,this.add_icon_hide_timeout&&clearTimeout(this.add_icon_hide_timeout),this.add_icon_hide_timeout=setTimeout(()=>{this.is_hovering_icon||this.$add_icon_group.classList.add("hide")},200))}),u.on(this.group,"mousedown",()=>{this.is_dragging=!0,this.hide_add_icon()}),u.on(this.group,"click",()=>{this.action_completed||this.gantt.bar_being_dragged||this.gantt.trigger_event("click",[this.task])}),u.on(this.group,"dblclick",s=>{this.action_completed||(this.group.classList.remove("active"),this.gantt.popup&&this.gantt.popup.parent.classList.remove("hide"),this.gantt.trigger_event("double_click",[this.task]))});let i=!1;u.on(this.group,"touchstart",s=>{if(!i)return i=!0,setTimeout(function(){i=!1},300),!1;s.preventDefault(),!this.action_completed&&(this.group.classList.remove("active"),this.gantt.popup&&this.gantt.popup.parent.classList.remove("hide"),this.gantt.trigger_event("double_click",[this.task]))})}update_bar_position({x:t=null,width:e=null}){const i=this.$bar;if(t){if(!this.validate_dependency_constraints(t,e))return;this.update_attr(i,"x",t),this.x=t,this.$date_highlight.style.left=t+"px"}e>0&&(this.update_attr(i,"width",e),this.$date_highlight.style.width=e+"px"),this.update_label_position(),this.update_handle_position(),this.update_add_icon_position(),this.date_changed(),this.compute_duration(),this.gantt.options.show_expected_progress&&this.update_expected_progressbar_position(),this.update_progressbar_position(),this.update_arrow_position()}validate_dependency_constraints(t,e=null){const i=this.task.dependencies_type||this.gantt.options.dependencies_type;if(i==="fixed")return this.task.dependencies.map(_=>this.gantt.get_bar(_).$bar.getX()).reduce((_,l)=>_&&t>=l,!0);const s=t/this.gantt.config.column_width,n=c.add(this.gantt.gantt_start,s*this.gantt.config.step,this.gantt.config.unit),a=(e||this.$bar.getWidth())/this.gantt.config.column_width,h=c.add(n,a*this.gantt.config.step,this.gantt.config.unit);for(const d of this.task.dependencies){const _=this.gantt.get_bar(d);if(!_)continue;const l=_.task;switch(i){case"finish-to-start":if(n<l._end)return!1;break;case"start-to-start":if(n<l._start)return!1;break;case"finish-to-finish":if(h<l._end)return!1;break;case"start-to-finish":if(h<l._start)return!1;break}}return!0}update_label_position_on_horizontal_scroll({x:t,sx:e}){const i=this.gantt.$container,s=this.group.querySelector(".bar-label"),n=this.group.querySelector(".bar-img")||"",o=this.bar_group.querySelector(".img_mask")||"";let a=this.$bar.getX()+this.$bar.getWidth(),h=s.getX()+t,d=n&&n.getX()+t||0,_=n&&n.getBBox().width+7||7,l=h+s.getBBox().width+7,p=e+i.clientWidth/2;s.classList.contains("big")||(l<a&&t>0&&l<p||h-_>this.$bar.getX()&&t<0&&l>p)&&(s.setAttribute("x",h),n&&(n.setAttribute("x",d),o.setAttribute("x",d)))}date_changed(){let t=!1;const{new_start_date:e,new_end_date:i}=this.compute_start_end_date();Number(this.task._start)!==Number(e)&&(t=!0,this.task._start=e),Number(this.task._end)!==Number(i)&&(t=!0,this.task._end=i),t&&this.gantt.trigger_event("date_change",[this.task,e,c.add(i,-1,"second")])}progress_changed(){this.task.progress=this.compute_progress(),this.gantt.trigger_event("progress_change",[this.task,this.task.progress])}set_action_completed(){this.action_completed=!0,setTimeout(()=>this.action_completed=!1,1e3),this.is_dragging=!1,this.$add_icon_group&&this.is_hovering_bar&&this.$add_icon_group.classList.remove("hide")}compute_start_end_date(){const t=this.$bar,e=t.getX()/this.gantt.config.column_width;let i=c.add(this.gantt.gantt_start,e*this.gantt.config.step,this.gantt.config.unit);const s=t.getWidth()/this.gantt.config.column_width,n=c.add(i,s*this.gantt.config.step,this.gantt.config.unit);return{new_start_date:i,new_end_date:n}}compute_progress(){this.progress_width=this.$bar_progress.getWidth(),this.x=this.$bar_progress.getBBox().x;const t=this.x+this.progress_width,e=this.progress_width-this.gantt.config.ignored_positions.reduce((s,n)=>s+(n>=this.x&&n<=t),0)*this.gantt.config.column_width;if(e<0)return 0;const i=this.$bar.getWidth()-this.ignored_duration_raw*this.gantt.config.column_width;return parseInt(e/i*100,10)}compute_expected_progress(){this.expected_progress=c.diff(c.today(),this.task._start,"hour")/this.gantt.config.step,this.expected_progress=(this.expected_progress<this.duration?this.expected_progress:this.duration)*100/this.duration}compute_x(){const{column_width:t}=this.gantt.config,e=this.task._start,i=this.gantt.gantt_start;let n=c.diff(e,i,this.gantt.config.unit)/this.gantt.config.step*t;this.x=n}compute_y(){this.y=this.gantt.config.header_height+this.gantt.options.padding/2+this.task._index*(this.height+this.gantt.options.padding)}compute_duration(){let t=0,e=0;for(let i=new Date(this.task._start);i<this.task._end;i.setDate(i.getDate()+1))e++,!this.gantt.config.ignored_dates.find(s=>s.getTime()===i.getTime())&&(!this.gantt.config.ignored_function||!this.gantt.config.ignored_function(i))&&t++;this.task.actual_duration=t,this.task.ignored_duration=e-t,this.duration=c.convert_scales(e+"d",this.gantt.config.unit)/this.gantt.config.step,this.actual_duration_raw=c.convert_scales(t+"d",this.gantt.config.unit)/this.gantt.config.step,this.ignored_duration_raw=this.duration-this.actual_duration_raw}update_attr(t,e,i){return i=+i,isNaN(i)||t.setAttribute(e,i),t}update_expected_progressbar_position(){this.invalid||(this.$expected_bar_progress.setAttribute("x",this.$bar.getX()),this.compute_expected_progress(),this.$expected_bar_progress.setAttribute("width",this.gantt.config.column_width*this.actual_duration_raw*(this.expected_progress/100)||0))}update_progressbar_position(){this.invalid||this.gantt.options.readonly||(this.$bar_progress.setAttribute("x",this.$bar.getX()),this.$bar_progress.setAttribute("width",this.calculate_progress_width()))}update_label_position(){const t=this.bar_group.querySelector(".img_mask")||"",e=this.$bar,i=this.group.querySelector(".bar-label"),s=this.group.querySelector(".bar-img");let n=5,o=this.image_size+10;const a=i.getBBox().width,h=e.getWidth();a>h?(i.classList.add("big"),s?(s.setAttribute("x",e.getEndX()+n),t.setAttribute("x",e.getEndX()+n),i.setAttribute("x",e.getEndX()+o)):i.setAttribute("x",e.getEndX()+n)):(i.classList.remove("big"),s?(s.setAttribute("x",e.getX()+n),t.setAttribute("x",e.getX()+n),i.setAttribute("x",e.getX()+h/2+o)):i.setAttribute("x",e.getX()+h/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()),this.handle_group.querySelector(".handle.right").setAttribute("x",t.getEndX());const e=this.group.querySelector(".handle.progress");e&&e.setAttribute("cx",this.$bar_progress.getEndX())}update_add_icon_position(){if(!this.$add_icon_group)return;const t=this.icon_size,e=this.icon_padding;let i;if(this.gantt.options.task_add_icon_position==="before")i=this.x-t-e;else if(this.gantt.options.task_add_icon_position==="after")i=this.x+this.$bar.getWidth()+e;else return;const s=this.y+(this.height-t)/2;this.$add_icon_circle.setAttribute("cx",i+t/2),this.$add_icon_circle.setAttribute("cy",s+t/2),this.$add_icon_vertical.setAttribute("x1",i+t/2),this.$add_icon_vertical.setAttribute("y1",s+5),this.$add_icon_vertical.setAttribute("x2",i+t/2),this.$add_icon_vertical.setAttribute("y2",s+t-5),this.$add_icon_horizontal.setAttribute("x1",i+5),this.$add_icon_horizontal.setAttribute("y1",s+t/2),this.$add_icon_horizontal.setAttribute("x2",i+t-5),this.$add_icon_horizontal.setAttribute("y2",s+t/2)}hide_add_icon(){this.$add_icon_group&&this.$add_icon_group.classList.add("hide")}update_arrow_position(){this.arrows=this.arrows||[];for(let t of this.arrows)t.update()}}class F{constructor(t,e,i){this.parent=t,this.popup_func=e,this.gantt=i,this.make()}make(){this.parent.innerHTML=`
|
|
20
20
|
<div class="title"></div>
|
|
21
21
|
<div class="subtitle"></div>
|
|
22
22
|
<div class="details"></div>
|
|
23
23
|
<div class="actions"></div>
|
|
24
|
-
`,this.hide(),this.title=this.parent.querySelector(".title"),this.subtitle=this.parent.querySelector(".subtitle"),this.details=this.parent.querySelector(".details"),this.actions=this.parent.querySelector(".actions")}show({x:t,y:e,task:i,target:s}){this.actions.innerHTML="";let n=this.popup_func({task:i,chart:this.gantt,get_title:()=>this.title,set_title:a=>this.title.innerHTML=a,get_subtitle:()=>this.subtitle,set_subtitle:a=>this.subtitle.innerHTML=a,get_details:()=>this.details,set_details:a=>this.details.innerHTML=a,add_action:(a,o)=>{let h=this.gantt.create_el({classes:"action-btn",type:"button",append_to:this.actions});typeof a=="function"&&(a=a(i)),h.innerHTML=a,h.onclick=d=>o(i,this.gantt,d)}});n!==!1&&(n&&(this.parent.innerHTML=n),this.actions.innerHTML===""?this.actions.remove():this.parent.appendChild(this.actions),this.parent.style.left=t+10+"px",this.parent.style.top=e-10+"px",this.parent.classList.remove("hide"))}hide(){this.parent.classList.add("hide")}}function S(r){const t=r.getFullYear();return t-t%10+""}function I(r,t,e){let i=l.add(r,6,"day"),s=i.getMonth()!==r.getMonth()?"D MMM":"D",n=!t||r.getMonth()!==t.getMonth()?"D MMM":"D";return`${l.format(r,n,e)} - ${l.format(i,s,e)}`}const m=[{name:"Hour",padding:"7d",step:"1h",date_format:"YYYY-MM-DD HH:",lower_text:"HH",upper_text:(r,t,e)=>!t||r.getDate()!==t.getDate()?l.format(r,"D MMMM",e):"",upper_text_frequency:24},{name:"Quarter Day",padding:"7d",step:"6h",date_format:"YYYY-MM-DD HH:",lower_text:"HH",upper_text:(r,t,e)=>!t||r.getDate()!==t.getDate()?l.format(r,"D MMM",e):"",upper_text_frequency:4},{name:"Half Day",padding:"14d",step:"12h",date_format:"YYYY-MM-DD HH:",lower_text:"HH",upper_text:(r,t,e)=>!t||r.getDate()!==t.getDate()?r.getMonth()!==r.getMonth()?l.format(r,"D MMM",e):l.format(r,"D",e):"",upper_text_frequency:2},{name:"Day",padding:"7d",date_format:"YYYY-MM-DD",step:"1d",lower_text:(r,t,e)=>!t||r.getDate()!==t.getDate()?l.format(r,"D",e):"",upper_text:(r,t,e)=>!t||r.getMonth()!==t.getMonth()?l.format(r,"MMMM",e):"",thick_line:r=>r.getDay()===1},{name:"Week",padding:"1m",step:"7d",date_format:"YYYY-MM-DD",column_width:140,lower_text:I,upper_text:(r,t,e)=>!t||r.getMonth()!==t.getMonth()?l.format(r,"MMMM",e):"",thick_line:r=>r.getDate()>=1&&r.getDate()<=7,upper_text_frequency:4},{name:"Month",padding:"2m",step:"1m",column_width:120,date_format:"YYYY-MM",lower_text:"MMMM",upper_text:(r,t,e)=>!t||r.getFullYear()!==t.getFullYear()?l.format(r,"YYYY",e):"",thick_line:r=>r.getMonth()%3===0,snap_at:"7d"},{name:"Year",padding:"2y",step:"1y",column_width:120,date_format:"YYYY",upper_text:(r,t,e)=>!t||S(r)!==S(t)?S(r):"",lower_text:"YYYY",snap_at:"30d"}],j={arrow_curve:5,auto_move_label:!1,bar_corner_radius:3,bar_height:30,container_height:"auto",column_width:null,critical_path:!1,date_format:"YYYY-MM-DD HH:mm",dependencies_type:"fixed",upper_header_height:45,lower_header_height:30,snap_at:null,infinite_padding:!0,holidays:{"var(--g-weekend-highlight-color)":"weekend"},ignore:[],language:"en",lines:"both",move_dependencies:!0,padding:18,popup:r=>{r.set_title(r.task.name),r.task.description?r.set_subtitle(r.task.description):r.set_subtitle("");const t=l.format(r.task._start,"MMM D",r.chart.options.language),e=l.format(l.add(r.task._end,-1,"second"),"MMM D",r.chart.options.language);r.set_details(`${t} - ${e} (${r.task.actual_duration} days${r.task.ignored_duration?" + "+r.task.ignored_duration+" excluded":""})<br/>Progress: ${Math.floor(r.task.progress*100)/100}%`)},popup_on:"click",readonly_progress:!1,readonly_dates:!1,readonly:!1,scroll_to:"today",show_expected_progress:!1,today_button:!0,view_mode:"Day",view_mode_select:!1,view_modes:m,is_weekend:r=>r.getDay()===0||r.getDay()===6};class X{constructor(t,e,i){this.setup_wrapper(t),this.setup_options(i),this.setup_tasks(e),this.change_view_mode(),this.bind_events()}setup_wrapper(t){let e,i;if(typeof t=="string"){let s=document.querySelector(t);if(!s)throw new ReferenceError(`CSS selector "${t}" could not be found in DOM`);t=s}if(t instanceof HTMLElement)i=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=f("svg",{append_to:i,class:"gantt"}),this.$container=this.create_el({classes:"gantt-container",append_to:this.$svg.parentElement}),this.$container.appendChild(this.$svg),this.$popup_wrapper=this.create_el({classes:"popup-wrapper",append_to:this.$container})}setup_options(t){this.original_options=t,t!=null&&t.view_modes&&(t.view_modes=t.view_modes.map(i=>{if(typeof i=="string"){const s=m.find(n=>n.name===i);return s||console.error(`The view mode "${i}" is not predefined in Frappe Gantt. Please define the view mode object instead.`),s}return i}),t.view_mode=t.view_modes[0]),this.options={...j,...t};const e={"grid-height":"container_height","bar-height":"bar_height","lower-header-height":"lower_header_height","upper-header-height":"upper_header_height"};for(let i in e){let s=this.options[e[i]];s!=="auto"&&this.$container.style.setProperty("--gv-"+i,s+"px")}if(this.config={ignored_dates:[],ignored_positions:[],extend_by_units:10},typeof this.options.ignore!="function"){typeof this.options.ignore=="string"&&(this.options.ignore=[this.options.ignore]);for(let i of this.options.ignore){if(typeof i=="function"){this.config.ignored_function=i;continue}typeof i=="string"&&(i==="weekend"?this.config.ignored_function=s=>s.getDay()==6||s.getDay()==0:this.config.ignored_dates.push(new Date(i+" ")))}}else this.config.ignored_function=this.options.ignore}update_options(t){this.setup_options({...this.original_options,...t}),this.change_view_mode(void 0,!0)}setup_tasks(t){this.tasks=t.map((e,i)=>{if(!e.start)return console.error(`task "${e.id}" doesn't have a start date`),!1;if(e._start=l.parse(e.start),e.end===void 0&&e.duration!==void 0&&(e.end=e._start,e.duration.split(" ").forEach(o=>{let{duration:h,scale:d}=l.parse_duration(o);e.end=l.add(e.end,h,d)})),!e.end)return console.error(`task "${e.id}" doesn't have an end date`),!1;if(e._end=l.parse(e.end),l.diff(e._end,e._start,"year")<0)return console.error(`start of task can't be after end of task: in task "${e.id}"`),!1;if(l.diff(e._end,e._start,"year")>10)return console.error(`the duration of task "${e.id}" is too long (above ten years)`),!1;if(e._index=i,l.get_date_values(e._end).slice(3).every(a=>a===0)&&(e._end=l.add(e._end,24,"hour")),typeof e.dependencies=="string"||!e.dependencies){let a=[];e.dependencies&&(a=e.dependencies.split(",").map(o=>o.trim().replaceAll(" ","_")).filter(o=>o)),e.dependencies=a}return e.id?typeof e.id=="string"?e.id=e.id.replaceAll(" ","_"):e.id=`${e.id}`:e.id=z(e),e}).filter(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()}update_task(t,e){let i=this.tasks.find(n=>n.id===t),s=this.bars[i._index];Object.assign(i,e),s.refresh()}change_view_mode(t=this.options.view_mode,e=!1){typeof t=="string"&&(t=this.options.view_modes.find(n=>n.name===t));let i,s;e&&(i=this.$container.scrollLeft,s=this.options.scroll_to,this.options.scroll_to=null),this.options.view_mode=t.name,this.config.view_mode=t,this.update_view_scale(t),this.setup_dates(e),this.render(),e&&(this.$container.scrollLeft=i,this.options.scroll_to=s),this.trigger_event("view_change",[t])}update_view_scale(t){let{duration:e,scale:i}=l.parse_duration(t.step);this.config.step=e,this.config.unit=i,this.config.column_width=this.options.column_width||t.column_width||45,this.$container.style.setProperty("--gv-column-width",this.config.column_width+"px"),this.config.header_height=this.options.lower_header_height+this.options.upper_header_height+10}setup_dates(t=!1){this.setup_gantt_dates(t),this.setup_date_values()}setup_gantt_dates(t){let e,i;this.tasks.length||(e=new Date,i=new Date);for(let s of this.tasks)(!e||s._start<e)&&(e=s._start),(!i||s._end>i)&&(i=s._end);if(e=l.start_of(e,this.config.unit),i=l.start_of(i,this.config.unit),!t)if(this.options.infinite_padding)this.gantt_start=l.add(e,-this.config.extend_by_units*3,this.config.unit),this.gantt_end=l.add(i,this.config.extend_by_units*3,this.config.unit);else{typeof this.config.view_mode.padding=="string"&&(this.config.view_mode.padding=[this.config.view_mode.padding,this.config.view_mode.padding]);let[s,n]=this.config.view_mode.padding.map(l.parse_duration);this.gantt_start=l.add(e,-s.duration,s.scale),this.gantt_end=l.add(i,n.duration,n.scale)}this.config.date_format=this.config.view_mode.date_format||this.options.date_format,this.gantt_start.setHours(0,0,0,0)}setup_date_values(){let t=this.gantt_start;for(this.dates=[t];t<this.gantt_end;)t=l.add(t,this.config.step,this.config.unit),this.dates.push(t)}bind_events(){this.bind_grid_click(),this.bind_holiday_labels(),this.bind_bar_events()}render(){this.clear(),this.setup_layers(),this.make_grid(),this.make_dates(),this.make_grid_extras(),this.make_bars(),this.make_arrows(),this.map_arrows_on_bars(),this.set_dimensions(),this.set_scroll_position(this.options.scroll_to)}setup_layers(){this.layers={};const t=["grid","arrow","progress","bar"];for(let e of t)this.layers[e]=f("g",{class:e,append_to:this.$svg});this.$extras=this.create_el({classes:"extras",append_to:this.$container}),this.$adjust=this.create_el({classes:"adjust hide",append_to:this.$extras,type:"button"}),this.$adjust.innerHTML="←"}make_grid(){this.make_grid_background(),this.make_grid_rows(),this.make_grid_header(),this.make_side_header()}make_grid_extras(){this.make_grid_highlights(),this.make_grid_ticks()}make_grid_background(){const t=this.dates.length*this.config.column_width,e=Math.max(this.config.header_height+this.options.padding+(this.options.bar_height+this.options.padding)*this.tasks.length-10,this.options.container_height!=="auto"?this.options.container_height:0);f("rect",{x:0,y:0,width:t,height:e,class:"grid-background",append_to:this.$svg}),p.attr(this.$svg,{height:e,width:"100%"}),this.grid_height=e,this.options.container_height==="auto"&&(this.$container.style.height=e+"px")}make_grid_rows(){const t=f("g",{append_to:this.layers.grid}),e=this.dates.length*this.config.column_width,i=this.options.bar_height+this.options.padding;this.config.header_height;for(let s=this.config.header_height;s<this.grid_height;s+=i)f("rect",{x:0,y:s,width:e,height:i,class:"grid-row",append_to:t})}make_grid_header(){this.$header=this.create_el({width:this.dates.length*this.config.column_width,classes:"grid-header",append_to:this.$container}),this.$upper_header=this.create_el({classes:"upper-header",append_to:this.$header}),this.$lower_header=this.create_el({classes:"lower-header",append_to:this.$header})}make_side_header(){if(this.$side_header=this.create_el({classes:"side-header"}),this.$upper_header.prepend(this.$side_header),this.options.view_mode_select){const t=document.createElement("select");t.classList.add("viewmode-select");const e=document.createElement("option");e.selected=!0,e.disabled=!0,e.textContent="Mode",t.appendChild(e);for(const i of this.options.view_modes){const s=document.createElement("option");s.value=i.name,s.textContent=i.name,i.name===this.config.view_mode.name&&(s.selected=!0),t.appendChild(s)}t.addEventListener("change",(function(){this.change_view_mode(t.value,!0)}).bind(this)),this.$side_header.appendChild(t)}if(this.options.today_button){let t=document.createElement("button");t.classList.add("today-button"),t.textContent="Today",t.onclick=this.scroll_current.bind(this),this.$side_header.prepend(t),this.$today_button=t}}make_grid_ticks(){if(this.options.lines==="none")return;let t=0,e=this.config.header_height,i=this.grid_height-this.config.header_height,s=f("g",{class:"lines_layer",append_to:this.layers.grid}),n=this.config.header_height;const a=this.dates.length*this.config.column_width,o=this.options.bar_height+this.options.padding;if(this.options.lines!=="vertical")for(let h=this.config.header_height;h<this.grid_height;h+=o)f("line",{x1:0,y1:n+o,x2:a,y2:n+o,class:"row-line",append_to:s}),n+=o;if(this.options.lines!=="horizontal")for(let h of this.dates){let d="tick";this.config.view_mode.thick_line&&this.config.view_mode.thick_line(h)&&(d+=" thick"),f("path",{d:`M ${t} ${e} v ${i}`,class:d,append_to:this.layers.grid}),this.view_is("month")?t+=l.get_days_in_month(h)*this.config.column_width/30:this.view_is("year")?t+=l.get_days_in_year(h)*this.config.column_width/365:t+=this.config.column_width}}highlight_holidays(){let t={};if(this.options.holidays)for(let e in this.options.holidays){let i=this.options.holidays[e];i==="weekend"&&(i=this.options.is_weekend);let s;if(typeof i=="object"){let n=i.find(a=>typeof a=="function");if(n&&(s=n),this.options.holidays.name){let a=new Date(i.date+" ");i=o=>a.getTime()===o.getTime(),t[a]=i.name}else i=a=>this.options.holidays[e].filter(o=>typeof o!="function").map(o=>{if(o.name){let h=new Date(o.date+" ");return t[h]=o.name,h.getTime()}return new Date(o+" ").getTime()}).includes(a.getTime())}for(let n=new Date(this.gantt_start);n<=this.gantt_end;n.setDate(n.getDate()+1))if(!(this.config.ignored_dates.find(a=>a.getTime()==n.getTime())||this.config.ignored_function&&this.config.ignored_function(n))&&(i(n)||s&&s(n))){const a=l.diff(n,this.gantt_start,this.config.unit)/this.config.step*this.config.column_width,o=this.grid_height-this.config.header_height,h=l.format(n,"YYYY-MM-DD",this.options.language).replace(" ","_");if(t[n]){let d=this.create_el({classes:"holiday-label label_"+h,append_to:this.$extras});d.textContent=t[n]}f("rect",{x:Math.round(a),y:this.config.header_height,width:this.config.column_width/l.convert_scales(this.config.view_mode.step,"day"),height:o,class:"holiday-highlight "+h,style:`fill: ${e};`,append_to:this.layers.grid})}}}highlight_current(){const t=this.get_closest_date();if(!t)return;const[e,i]=t;i.classList.add("current-date-highlight");const n=l.diff(new Date,this.gantt_start,this.config.unit)/this.config.step*this.config.column_width;this.$current_highlight=this.create_el({top:this.config.header_height,left:n,height:this.grid_height-this.config.header_height,classes:"current-highlight",append_to:this.$container}),this.$current_ball_highlight=this.create_el({top:this.config.header_height-6,left:n-2.5,width:6,height:6,classes:"current-ball-highlight",append_to:this.$header})}make_grid_highlights(){this.highlight_holidays(),this.config.ignored_positions=[];const t=(this.options.bar_height+this.options.padding)*this.tasks.length;this.layers.grid.innerHTML+=`<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4">
|
|
24
|
+
`,this.hide(),this.title=this.parent.querySelector(".title"),this.subtitle=this.parent.querySelector(".subtitle"),this.details=this.parent.querySelector(".details"),this.actions=this.parent.querySelector(".actions")}show({x:t,y:e,task:i,target:s}){this.actions.innerHTML="";let n=this.popup_func({task:i,chart:this.gantt,get_title:()=>this.title,set_title:o=>this.title.innerHTML=o,get_subtitle:()=>this.subtitle,set_subtitle:o=>this.subtitle.innerHTML=o,get_details:()=>this.details,set_details:o=>this.details.innerHTML=o,add_action:(o,a)=>{let h=this.gantt.create_el({classes:"action-btn",type:"button",append_to:this.actions});typeof o=="function"&&(o=o(i)),h.innerHTML=o,h.onclick=d=>a(i,this.gantt,d)}});n!==!1&&(n&&(this.parent.innerHTML=n),this.actions.innerHTML===""?this.actions.remove():this.parent.appendChild(this.actions),this.parent.style.left=t+10+"px",this.parent.style.top=e-10+"px",this.parent.classList.remove("hide"))}hide(){this.parent.classList.add("hide")}}function S(r){const t=r.getFullYear();return t-t%10+""}function O(r,t,e){let i=c.add(r,6,"day"),s=i.getMonth()!==r.getMonth()?"D MMM":"D",n=!t||r.getMonth()!==t.getMonth()?"D MMM":"D";return`${c.format(r,n,e)} - ${c.format(i,s,e)}`}const w=[{name:"Hour",padding:"7d",step:"1h",date_format:"YYYY-MM-DD HH:",lower_text:"HH",upper_text:(r,t,e)=>!t||r.getDate()!==t.getDate()?c.format(r,"D MMMM",e):"",upper_text_frequency:24},{name:"Quarter Day",padding:"7d",step:"6h",date_format:"YYYY-MM-DD HH:",lower_text:"HH",upper_text:(r,t,e)=>!t||r.getDate()!==t.getDate()?c.format(r,"D MMM",e):"",upper_text_frequency:4},{name:"Half Day",padding:"14d",step:"12h",date_format:"YYYY-MM-DD HH:",lower_text:"HH",upper_text:(r,t,e)=>!t||r.getDate()!==t.getDate()?r.getMonth()!==r.getMonth()?c.format(r,"D MMM",e):c.format(r,"D",e):"",upper_text_frequency:2},{name:"Day",padding:"7d",date_format:"YYYY-MM-DD",step:"1d",lower_text:(r,t,e)=>!t||r.getDate()!==t.getDate()?c.format(r,"D",e):"",upper_text:(r,t,e)=>!t||r.getMonth()!==t.getMonth()?c.format(r,"MMMM",e):"",thick_line:r=>r.getDay()===1},{name:"Week",padding:"1m",step:"7d",date_format:"YYYY-MM-DD",column_width:140,lower_text:O,upper_text:(r,t,e)=>!t||r.getMonth()!==t.getMonth()?c.format(r,"MMMM",e):"",thick_line:r=>r.getDate()>=1&&r.getDate()<=7,upper_text_frequency:4},{name:"Month",padding:"2m",step:"1m",column_width:120,date_format:"YYYY-MM",lower_text:"MMMM",upper_text:(r,t,e)=>!t||r.getFullYear()!==t.getFullYear()?c.format(r,"YYYY",e):"",thick_line:r=>r.getMonth()%3===0,snap_at:"7d"},{name:"Year",padding:"2y",step:"1y",column_width:120,date_format:"YYYY",upper_text:(r,t,e)=>!t||S(r)!==S(t)?S(r):"",lower_text:"YYYY",snap_at:"30d"}],I={arrow_curve:5,auto_move_label:!1,bar_corner_radius:3,bar_height:30,container_height:"auto",column_width:null,critical_path:!1,date_format:"YYYY-MM-DD HH:mm",dependencies_type:"fixed",upper_header_height:45,lower_header_height:30,snap_at:null,infinite_padding:!0,holidays:{"var(--g-weekend-highlight-color)":"weekend"},ignore:[],language:"en",lines:"both",move_dependencies:!0,padding:18,popup:r=>{r.set_title(r.task.name),r.task.description?r.set_subtitle(r.task.description):r.set_subtitle("");const t=c.format(r.task._start,"MMM D",r.chart.options.language),e=c.format(c.add(r.task._end,-1,"second"),"MMM D",r.chart.options.language);r.set_details(`${t} - ${e} (${r.task.actual_duration} days${r.task.ignored_duration?" + "+r.task.ignored_duration+" excluded":""})<br/>Progress: ${Math.floor(r.task.progress*100)/100}%`)},popup_on:"click",readonly_progress:!1,readonly_dates:!1,readonly:!1,scroll_to:"today",show_expected_progress:!1,task_add_icon_position:null,today_button:!0,view_mode:"Day",view_mode_select:!1,view_modes:w,is_weekend:r=>r.getDay()===0||r.getDay()===6};class X{constructor(t,e,i){this.setup_wrapper(t),this.setup_options(i),this.setup_tasks(e),this.change_view_mode(),this.bind_events()}setup_wrapper(t){let e,i;if(typeof t=="string"){let s=document.querySelector(t);if(!s)throw new ReferenceError(`CSS selector "${t}" could not be found in DOM`);t=s}if(t instanceof HTMLElement)i=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=f("svg",{append_to:i,class:"gantt"}),this.$container=this.create_el({classes:"gantt-container",append_to:this.$svg.parentElement}),this.$container.appendChild(this.$svg),this.$popup_wrapper=this.create_el({classes:"popup-wrapper",append_to:this.$container})}setup_options(t){this.original_options=t,t!=null&&t.view_modes&&(t.view_modes=t.view_modes.map(i=>{if(typeof i=="string"){const s=w.find(n=>n.name===i);return s||console.error(`The view mode "${i}" is not predefined in Frappe Gantt. Please define the view mode object instead.`),s}return i}),t.view_mode=t.view_modes[0]),this.options={...I,...t};const e={"grid-height":"container_height","bar-height":"bar_height","lower-header-height":"lower_header_height","upper-header-height":"upper_header_height"};for(let i in e){let s=this.options[e[i]];s!=="auto"&&this.$container.style.setProperty("--gv-"+i,s+"px")}if(this.config={ignored_dates:[],ignored_positions:[],extend_by_units:10},typeof this.options.ignore!="function"){typeof this.options.ignore=="string"&&(this.options.ignore=[this.options.ignore]);for(let i of this.options.ignore){if(typeof i=="function"){this.config.ignored_function=i;continue}typeof i=="string"&&(i==="weekend"?this.config.ignored_function=s=>s.getDay()==6||s.getDay()==0:this.config.ignored_dates.push(new Date(i+" ")))}}else this.config.ignored_function=this.options.ignore}update_options(t){this.setup_options({...this.original_options,...t}),this.change_view_mode(void 0,!0)}setup_tasks(t){this.tasks=t.map((e,i)=>{if(!e.start)return console.error(`task "${e.id}" doesn't have a start date`),!1;if(e._start=c.parse(e.start),e.end===void 0&&e.duration!==void 0&&(e.end=e._start,e.duration.split(" ").forEach(a=>{let{duration:h,scale:d}=c.parse_duration(a);e.end=c.add(e.end,h,d)})),!e.end)return console.error(`task "${e.id}" doesn't have an end date`),!1;if(e._end=c.parse(e.end),c.diff(e._end,e._start,"year")<0)return console.error(`start of task can't be after end of task: in task "${e.id}"`),!1;if(c.diff(e._end,e._start,"year")>10)return console.error(`the duration of task "${e.id}" is too long (above ten years)`),!1;if(e._index=i,c.get_date_values(e._end).slice(3).every(o=>o===0)&&(e._end=c.add(e._end,24,"hour")),typeof e.dependencies=="string"||!e.dependencies){let o=[];e.dependencies&&(o=e.dependencies.split(",").map(a=>a.trim().replaceAll(" ","_")).filter(a=>a)),e.dependencies=o}return e.id?typeof e.id=="string"?e.id=e.id.replaceAll(" ","_"):e.id=`${e.id}`:e.id=j(e),e}).filter(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()}update_task(t,e){let i=this.tasks.find(n=>n.id===t),s=this.bars[i._index];Object.assign(i,e),s.refresh()}change_view_mode(t=this.options.view_mode,e=!1){typeof t=="string"&&(t=this.options.view_modes.find(n=>n.name===t));let i,s;e&&(i=this.$container.scrollLeft,s=this.options.scroll_to,this.options.scroll_to=null),this.options.view_mode=t.name,this.config.view_mode=t,this.update_view_scale(t),this.setup_dates(e),this.render(),e&&(this.$container.scrollLeft=i,this.options.scroll_to=s),this.trigger_event("view_change",[t])}update_view_scale(t){let{duration:e,scale:i}=c.parse_duration(t.step);this.config.step=e,this.config.unit=i,this.config.column_width=this.options.column_width||t.column_width||45,this.$container.style.setProperty("--gv-column-width",this.config.column_width+"px"),this.config.header_height=this.options.lower_header_height+this.options.upper_header_height+10}setup_dates(t=!1){this.setup_gantt_dates(t),this.setup_date_values()}setup_gantt_dates(t){let e,i;this.tasks.length||(e=new Date,i=new Date);for(let s of this.tasks)(!e||s._start<e)&&(e=s._start),(!i||s._end>i)&&(i=s._end);if(e=c.start_of(e,this.config.unit),i=c.start_of(i,this.config.unit),!t)if(this.options.infinite_padding)this.gantt_start=c.add(e,-this.config.extend_by_units*3,this.config.unit),this.gantt_end=c.add(i,this.config.extend_by_units*3,this.config.unit);else{typeof this.config.view_mode.padding=="string"&&(this.config.view_mode.padding=[this.config.view_mode.padding,this.config.view_mode.padding]);let[s,n]=this.config.view_mode.padding.map(c.parse_duration);this.gantt_start=c.add(e,-s.duration,s.scale),this.gantt_end=c.add(i,n.duration,n.scale)}this.config.date_format=this.config.view_mode.date_format||this.options.date_format,this.gantt_start.setHours(0,0,0,0)}setup_date_values(){let t=this.gantt_start;for(this.dates=[t];t<this.gantt_end;)t=c.add(t,this.config.step,this.config.unit),this.dates.push(t)}bind_events(){this.bind_grid_click(),this.bind_holiday_labels(),this.bind_bar_events()}render(){this.clear(),this.setup_layers(),this.make_grid(),this.make_dates(),this.make_grid_extras(),this.make_bars(),this.make_arrows(),this.map_arrows_on_bars(),this.set_dimensions(),this.set_scroll_position(this.options.scroll_to)}setup_layers(){this.layers={};const t=["grid","arrow","progress","bar"];for(let e of t)this.layers[e]=f("g",{class:e,append_to:this.$svg});this.$extras=this.create_el({classes:"extras",append_to:this.$container}),this.$adjust=this.create_el({classes:"adjust hide",append_to:this.$extras,type:"button"}),this.$adjust.innerHTML="←"}make_grid(){this.make_grid_background(),this.make_grid_rows(),this.make_grid_header(),this.make_side_header()}make_grid_extras(){this.make_grid_highlights(),this.make_grid_ticks()}make_grid_background(){const t=this.dates.length*this.config.column_width,e=Math.max(this.config.header_height+this.options.padding+(this.options.bar_height+this.options.padding)*this.tasks.length-10,this.options.container_height!=="auto"?this.options.container_height:0);f("rect",{x:0,y:0,width:t,height:e,class:"grid-background",append_to:this.$svg}),u.attr(this.$svg,{height:e,width:"100%"}),this.grid_height=e,this.options.container_height==="auto"&&(this.$container.style.height=e+"px")}make_grid_rows(){const t=f("g",{append_to:this.layers.grid}),e=this.dates.length*this.config.column_width,i=this.options.bar_height+this.options.padding;this.config.header_height;for(let s=this.config.header_height;s<this.grid_height;s+=i)f("rect",{x:0,y:s,width:e,height:i,class:"grid-row",append_to:t})}make_grid_header(){this.$header=this.create_el({width:this.dates.length*this.config.column_width,classes:"grid-header",append_to:this.$container}),this.$upper_header=this.create_el({classes:"upper-header",append_to:this.$header}),this.$lower_header=this.create_el({classes:"lower-header",append_to:this.$header})}make_side_header(){if(this.$side_header=this.create_el({classes:"side-header"}),this.$upper_header.prepend(this.$side_header),this.options.view_mode_select){const t=document.createElement("select");t.classList.add("viewmode-select");const e=document.createElement("option");e.selected=!0,e.disabled=!0,e.textContent="Mode",t.appendChild(e);for(const i of this.options.view_modes){const s=document.createElement("option");s.value=i.name,s.textContent=i.name,i.name===this.config.view_mode.name&&(s.selected=!0),t.appendChild(s)}t.addEventListener("change",(function(){this.change_view_mode(t.value,!0)}).bind(this)),this.$side_header.appendChild(t)}if(this.options.today_button){let t=document.createElement("button");t.classList.add("today-button"),t.textContent="Today",t.onclick=this.scroll_current.bind(this),this.$side_header.prepend(t),this.$today_button=t}}make_grid_ticks(){if(this.options.lines==="none")return;let t=0,e=this.config.header_height,i=this.grid_height-this.config.header_height,s=f("g",{class:"lines_layer",append_to:this.layers.grid}),n=this.config.header_height;const o=this.dates.length*this.config.column_width,a=this.options.bar_height+this.options.padding;if(this.options.lines!=="vertical")for(let h=this.config.header_height;h<this.grid_height;h+=a)f("line",{x1:0,y1:n+a,x2:o,y2:n+a,class:"row-line",append_to:s}),n+=a;if(this.options.lines!=="horizontal")for(let h of this.dates){let d="tick";this.config.view_mode.thick_line&&this.config.view_mode.thick_line(h)&&(d+=" thick"),f("path",{d:`M ${t} ${e} v ${i}`,class:d,append_to:this.layers.grid}),this.view_is("month")?t+=c.get_days_in_month(h)*this.config.column_width/30:this.view_is("year")?t+=c.get_days_in_year(h)*this.config.column_width/365:t+=this.config.column_width}}highlight_holidays(){let t={};if(this.options.holidays)for(let e in this.options.holidays){let i=this.options.holidays[e];i==="weekend"&&(i=this.options.is_weekend);let s;if(typeof i=="object"){let n=i.find(o=>typeof o=="function");if(n&&(s=n),this.options.holidays.name){let o=new Date(i.date+" ");i=a=>o.getTime()===a.getTime(),t[o]=i.name}else i=o=>this.options.holidays[e].filter(a=>typeof a!="function").map(a=>{if(a.name){let h=new Date(a.date+" ");return t[h]=a.name,h.getTime()}return new Date(a+" ").getTime()}).includes(o.getTime())}for(let n=new Date(this.gantt_start);n<=this.gantt_end;n.setDate(n.getDate()+1))if(!(this.config.ignored_dates.find(o=>o.getTime()==n.getTime())||this.config.ignored_function&&this.config.ignored_function(n))&&(i(n)||s&&s(n))){const o=c.diff(n,this.gantt_start,this.config.unit)/this.config.step*this.config.column_width,a=this.grid_height-this.config.header_height,h=c.format(n,"YYYY-MM-DD",this.options.language).replace(" ","_");if(t[n]){let d=this.create_el({classes:"holiday-label label_"+h,append_to:this.$extras});d.textContent=t[n]}f("rect",{x:Math.round(o),y:this.config.header_height,width:this.config.column_width/c.convert_scales(this.config.view_mode.step,"day"),height:a,class:"holiday-highlight "+h,style:`fill: ${e};`,append_to:this.layers.grid})}}}highlight_current(){const t=this.get_closest_date();if(!t)return;const[e,i]=t;i.classList.add("current-date-highlight");const n=c.diff(new Date,this.gantt_start,this.config.unit)/this.config.step*this.config.column_width;this.$current_highlight=this.create_el({top:this.config.header_height,left:n,height:this.grid_height-this.config.header_height,classes:"current-highlight",append_to:this.$container}),this.$current_ball_highlight=this.create_el({top:this.config.header_height-6,left:n-2.5,width:6,height:6,classes:"current-ball-highlight",append_to:this.$header})}make_grid_highlights(){this.highlight_holidays(),this.config.ignored_positions=[];const t=(this.options.bar_height+this.options.padding)*this.tasks.length;this.layers.grid.innerHTML+=`<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4">
|
|
25
25
|
<path d="M-1,1 l2,-2
|
|
26
26
|
M0,4 l4,-4
|
|
27
27
|
M3,5 l2,-2"
|
|
28
28
|
style="stroke:grey; stroke-width:0.3" />
|
|
29
|
-
</pattern>`;for(let i=new Date(this.gantt_start);i<=this.gantt_end;i.setDate(i.getDate()+1)){if(!this.config.ignored_dates.find(n=>n.getTime()==i.getTime())&&(!this.config.ignored_function||!this.config.ignored_function(i)))continue;let s=l.convert_scales(l.diff(i,this.gantt_start)+"d",this.config.unit)/this.config.step;this.config.ignored_positions.push(s*this.config.column_width),f("rect",{x:s*this.config.column_width,y:this.config.header_height,width:this.config.column_width,height:t,class:"ignored-bar",style:"fill: url(#diagonalHatch);",append_to:this.$svg})}this.highlight_current(this.config.view_mode)}create_el({left:t,top:e,width:i,height:s,id:n,classes:a,append_to:o,type:h}){let d=document.createElement(h||"div");for(let _ of a.split(" "))d.classList.add(_);return d.style.top=e+"px",d.style.left=t+"px",n&&(d.id=n),i&&(d.style.width=i+"px"),s&&(d.style.height=s+"px"),o&&o.appendChild(d),d}make_dates(){this.get_dates_to_draw().forEach((t,e)=>{if(t.lower_text){let i=this.create_el({left:t.x,top:t.lower_y,classes:"lower-text date_"+D(t.formatted_date),append_to:this.$lower_header});i.innerText=t.lower_text}if(t.upper_text){let i=this.create_el({left:t.x,top:t.upper_y,classes:"upper-text",append_to:this.$upper_header});i.innerText=t.upper_text}}),this.upperTexts=Array.from(this.$container.querySelectorAll(".upper-text"))}get_dates_to_draw(){let t=null;return this.dates.map((i,s)=>{const n=this.get_date_info(i,t,s);return t=n,n})}get_date_info(t,e){let i=e?e.date:null;this.config.column_width;const s=e?e.x+e.column_width:0;let n=this.config.view_mode.upper_text,a=this.config.view_mode.lower_text;return n?typeof n=="string"&&(this.config.view_mode.upper_text=o=>l.format(o,n,this.options.language)):this.config.view_mode.upper_text=()=>"",a?typeof a=="string"&&(this.config.view_mode.lower_text=o=>l.format(o,a,this.options.language)):this.config.view_mode.lower_text=()=>"",{date:t,formatted_date:D(l.format(t,this.config.date_format,this.options.language)),column_width:this.config.column_width,x:s,upper_text:this.config.view_mode.upper_text(t,i,this.options.language),lower_text:this.config.view_mode.lower_text(t,i,this.options.language),upper_y:17,lower_y:this.options.upper_header_height+5}}make_bars(){this.bars=this.tasks.map(t=>{const e=new F(this,t);return this.layers.bar.appendChild(e.group),e})}make_arrows(){this.arrows=[],this.options.critical_path&&this.calculate_critical_path();for(let t of this.tasks){let e=[];e=t.dependencies.map(i=>{const s=this.get_task(i);if(!s)return;const n=new q(this,this.bars[s._index],this.bars[t._index]);return this.layers.arrow.appendChild(n.element),n}).filter(Boolean),this.arrows=this.arrows.concat(e)}}calculate_critical_path(){this.tasks.forEach(n=>n._is_critical=!1);const t={};this.tasks.forEach(n=>{t[n.id]={es:0,ef:0,ls:0,lf:0}});const e=n=>{if(t[n.id].ef>0)return t[n.id];let a=0;n.dependencies&&n.dependencies.length>0&&n.dependencies.forEach(h=>{const d=this.get_task(h);if(d){const _=e(d);a=Math.max(a,_.ef)}}),t[n.id].es=a;const o=l.diff(n._end,n._start,"hour")/24;return t[n.id].ef=a+o,t[n.id]};this.tasks.forEach(n=>e(n));const i=Math.max(...Object.values(t).map(n=>n.ef)),s=n=>{if(t[n.id].ls>0||t[n.id].lf>0)return t[n.id];const a=this.tasks.filter(d=>d.dependencies&&d.dependencies.includes(n.id));let o=i;a.length>0&&a.forEach(d=>{const _=s(d);o=Math.min(o,_.ls)});const h=l.diff(n._end,n._start,"hour")/24;return t[n.id].lf=o,t[n.id].ls=o-h,t[n.id]};this.tasks.forEach(n=>s(n)),this.tasks.forEach(n=>{const a=t[n.id],o=a.ls-a.es;n._is_critical=Math.abs(o)<.01})}update_arrow_critical_path(){this.arrows.forEach(t=>{t.from_task.task._is_critical===!0&&t.to_task.task._is_critical===!0?t.element.classList.add("arrow-critical"):t.element.classList.remove("arrow-critical")})}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_dimensions(){const{width:t}=this.$svg.getBoundingClientRect(),e=this.$svg.querySelector(".grid .grid-row")?this.$svg.querySelector(".grid .grid-row").getAttribute("width"):0;t<e&&this.$svg.setAttribute("width",e)}set_scroll_position(t){if(this.options.infinite_padding&&(!t||t==="start")){let[a,...o]=this.get_start_end_positions();this.$container.scrollLeft=a;return}if(!t||t==="start")t=this.gantt_start;else if(t==="end")t=this.gantt_end;else{if(t==="today")return this.scroll_current();typeof t=="string"&&(t=l.parse(t))}const i=l.diff(t,this.gantt_start,this.config.unit)/this.config.step*this.config.column_width;this.$container.scrollTo({left:i-this.config.column_width/6,behavior:"smooth"}),this.$current&&this.$current.classList.remove("current-upper"),this.current_date=l.add(this.gantt_start,this.$container.scrollLeft/this.config.column_width,this.config.unit);let s=this.config.view_mode.upper_text(this.current_date,null,this.options.language),n=this.upperTexts.find(a=>a.textContent===s);this.current_date=l.add(this.gantt_start,(this.$container.scrollLeft+n.clientWidth)/this.config.column_width,this.config.unit),s=this.config.view_mode.upper_text(this.current_date,null,this.options.language),n=this.upperTexts.find(a=>a.textContent===s),n.classList.add("current-upper"),this.$current=n}scroll_current(){let t=this.get_closest_date();t&&this.set_scroll_position(t[0])}get_closest_date(){let t=new Date;if(t<this.gantt_start||t>this.gantt_end)return null;let e=new Date,i=this.$container.querySelector(".date_"+D(l.format(e,this.config.date_format,this.options.language))),s=0;for(;!i&&s<this.config.step;)e=l.add(e,-1,this.config.unit),i=this.$container.querySelector(".date_"+D(l.format(e,this.config.date_format,this.options.language))),s++;return[new Date(l.format(e,this.config.date_format,this.options.language)+" "),i]}bind_grid_click(){p.on(this.$container,"click",".grid-row, .grid-header, .ignored-bar, .holiday-highlight",()=>{this.unselect_all(),this.hide_popup()})}bind_holiday_labels(){const t=this.$container.querySelectorAll(".holiday-highlight");for(let e of t){const i=this.$container.querySelector(".label_"+e.classList[1]);if(!i)continue;let s;e.onmouseenter=n=>{s=setTimeout(()=>{i.classList.add("show"),i.style.left=(n.offsetX||n.layerX)+"px",i.style.top=(n.offsetY||n.layerY)+"px"},300)},e.onmouseleave=n=>{clearTimeout(s),i.classList.remove("show")}}}get_start_end_positions(){if(!this.bars.length)return[0,0,0];let{x:t,width:e}=this.bars[0].group.getBBox(),i=t,s=t,n=t+e;return Array.prototype.forEach.call(this.bars,function({group:a},o){let{x:h,width:d}=a.getBBox();h<i&&(i=h),h>s&&(s=h),h+d>n&&(n=h+d)}),[i,s,n]}bind_bar_events(){let t=!1,e=0,i=0,s=!1,n=!1,a=null,o=[];this.bar_being_dragged=null;const h=()=>t||s||n;this.$svg.onclick=_=>{_.target.classList.contains("grid-row")&&this.unselect_all()};let d=0;if(p.on(this.$svg,"mousemove",".bar-wrapper, .handle",_=>{this.bar_being_dragged===!1&&Math.abs((_.offsetX||_.layerX)-d)>10&&(this.bar_being_dragged=!0)}),p.on(this.$svg,"mousedown",".bar-wrapper, .handle",(_,c)=>{const g=p.closest(".bar-wrapper",c);c.classList.contains("left")?(s=!0,c.classList.add("visible")):c.classList.contains("right")?(n=!0,c.classList.add("visible")):c.classList.contains("bar-wrapper")&&(t=!0),this.popup&&this.popup.hide(),e=_.offsetX||_.layerX,a=g.getAttribute("data-id");const k=this.get_bar(a).task.dependencies_type||this.options.dependencies_type;let w;this.options.move_dependencies&&k==="fixed"?w=[a,...this.get_all_dependent_tasks(a)]:w=[a],o=w.map(v=>this.get_bar(v)),this.bar_being_dragged=!1,d=e,o.forEach(v=>{const b=v.$bar;b.ox=b.getX(),b.oy=b.getY(),b.owidth=b.getWidth(),b.finaldx=0})}),this.options.infinite_padding){let _=!1;p.on(this.$container,"mousewheel",c=>{let g=this.$container.scrollWidth/2;if(!_&&c.currentTarget.scrollLeft<=g){let u=c.currentTarget.scrollLeft;_=!0,this.gantt_start=l.add(this.gantt_start,-this.config.extend_by_units,this.config.unit),this.setup_date_values(),this.render(),c.currentTarget.scrollLeft=u+this.config.column_width*this.config.extend_by_units,setTimeout(()=>_=!1,300)}if(!_&&c.currentTarget.scrollWidth-(c.currentTarget.scrollLeft+c.currentTarget.clientWidth)<=g){let u=c.currentTarget.scrollLeft;_=!0,this.gantt_end=l.add(this.gantt_end,this.config.extend_by_units,this.config.unit),this.setup_date_values(),this.render(),c.currentTarget.scrollLeft=u,setTimeout(()=>_=!1,300)}})}p.on(this.$container,"scroll",_=>{let c=[];const g=this.bars.map(({group:y})=>y.getAttribute("data-id"));let u;i&&(u=_.currentTarget.scrollLeft-i),this.current_date=l.add(this.gantt_start,_.currentTarget.scrollLeft/this.config.column_width*this.config.step,this.config.unit);let k=this.config.view_mode.upper_text(this.current_date,null,this.options.language),w=this.upperTexts.find(y=>y.textContent===k);this.current_date=l.add(this.gantt_start,(_.currentTarget.scrollLeft+w.clientWidth)/this.config.column_width*this.config.step,this.config.unit),k=this.config.view_mode.upper_text(this.current_date,null,this.options.language),w=this.upperTexts.find(y=>y.textContent===k),w!==this.$current&&(this.$current&&this.$current.classList.remove("current-upper"),w.classList.add("current-upper"),this.$current=w),i=_.currentTarget.scrollLeft;let[v,b,B]=this.get_start_end_positions();i>B+100?(this.$adjust.innerHTML="←",this.$adjust.classList.remove("hide"),this.$adjust.onclick=()=>{this.$container.scrollTo({left:b,behavior:"smooth"})}):i+_.currentTarget.offsetWidth<v-100?(this.$adjust.innerHTML="→",this.$adjust.classList.remove("hide"),this.$adjust.onclick=()=>{this.$container.scrollTo({left:v,behavior:"smooth"})}):this.$adjust.classList.add("hide"),u&&(c=g.map(y=>this.get_bar(y)),this.options.auto_move_label&&c.forEach(y=>{y.update_label_position_on_horizontal_scroll({x:u,sx:_.currentTarget.scrollLeft})}))}),p.on(this.$svg,"mousemove",_=>{if(!h())return;const c=(_.offsetX||_.layerX)-e;o.forEach(g=>{const u=g.$bar;u.finaldx=this.get_snap_position(c,u.ox),this.hide_popup(),s?a===g.task.id?g.update_bar_position({x:u.ox+u.finaldx,width:u.owidth-u.finaldx}):g.update_bar_position({x:u.ox+u.finaldx}):n?a===g.task.id&&g.update_bar_position({width:u.owidth+u.finaldx}):t&&!this.options.readonly&&!this.options.readonly_dates&&g.update_bar_position({x:u.ox+u.finaldx})})}),document.addEventListener("mouseup",()=>{var _,c,g;t=!1,s=!1,n=!1,(g=(c=(_=this.$container.querySelector(".visible"))==null?void 0:_.classList)==null?void 0:c.remove)==null||g.call(c,"visible")}),p.on(this.$svg,"mouseup",_=>{this.bar_being_dragged=null,o.forEach(g=>{g.$bar.finaldx&&(g.date_changed(),g.compute_progress(),g.set_action_completed())});const c=this.get_bar(a);c&&c.$bar.finaldx&&this.update_dependent_tasks_by_type(c),this.options.critical_path&&o.some(g=>g.$bar.finaldx)&&(this.calculate_critical_path(),this.update_arrow_critical_path())}),this.bind_bar_progress()}bind_bar_progress(){let t=0,e=null,i=null,s=null,n=null;p.on(this.$svg,"mousedown",".handle.progress",(o,h)=>{e=!0,t=o.offsetX||o.layerX;const _=p.closest(".bar-wrapper",h).getAttribute("data-id");i=this.get_bar(_),s=i.$bar_progress,n=i.$bar,s.finaldx=0,s.owidth=s.getWidth(),s.min_dx=-s.owidth,s.max_dx=n.getWidth()-s.getWidth()});const a=this.config.ignored_positions.map(o=>[o,o+this.config.column_width]);p.on(this.$svg,"mousemove",o=>{if(!e)return;let h=o.offsetX||o.layerX;if(h>t){let c=a.find(([g,u])=>h>=g&&h<u);for(;c;)h=c[1],c=a.find(([g,u])=>h>=g&&h<u)}else{let c=a.find(([g,u])=>h>g&&h<=u);for(;c;)h=c[0],c=a.find(([g,u])=>h>g&&h<=u)}let _=h-t;console.log(s),_>s.max_dx&&(_=s.max_dx),_<s.min_dx&&(_=s.min_dx),s.setAttribute("width",s.owidth+_),p.attr(i.$handle_progress,"cx",s.getEndX()),s.finaldx=_}),p.on(this.$svg,"mouseup",()=>{e=!1,s&&s.finaldx&&(s.finaldx=0,i.progress_changed(),i.set_action_completed(),i=null,s=null,n=null)})}get_all_dependent_tasks(t){let e=[],i=[t];for(;i.length;){const s=i.reduce((n,a)=>(n=n.concat(this.dependency_map[a]),n),[]);e=e.concat(s),i=s.filter(n=>!i.includes(n))}return e.filter(Boolean)}update_dependent_tasks_by_type(t){if((t.task.dependencies_type||this.options.dependencies_type)==="fixed")return;(this.dependency_map[t.task.id]||[]).forEach(s=>{const n=this.get_bar(s);if(!n)return;const a=n.task,o=a.dependencies_type||this.options.dependencies_type;let h,d;const _=l.diff(a._end,a._start,"hour");let c=!1;switch(o){case"finish-to-start":t.task._end>a._start&&(h=new Date(t.task._end),d=l.add(h,_,"hour"),c=!0);break;case"start-to-start":t.task._start>a._start&&(h=new Date(t.task._start),d=l.add(h,_,"hour"),c=!0);break;case"finish-to-finish":t.task._end>a._end&&(d=new Date(t.task._end),h=l.add(d,-_,"hour"),c=!0);break;case"start-to-finish":t.task._start>a._end&&(d=new Date(t.task._start),h=l.add(d,-_,"hour"),c=!0);break;default:return}c&&(a._start=h,a._end=d,n.compute_x(),n.compute_duration(),n.update_bar_position({x:n.x,width:n.width}),this.trigger_event("date_change",[a,h,l.add(d,-1,"second")]),this.update_dependent_tasks_by_type(n))})}get_snap_position(t,e){let i=1;const s=this.options.snap_at||this.config.view_mode.snap_at||"1d";if(s!=="unit"){const{duration:_,scale:c}=l.parse_duration(s);i=l.convert_scales(this.config.view_mode.step,c)/_}const n=t%(this.config.column_width/i);let a=t-n+(n<this.config.column_width/i*2?0:this.config.column_width/i),o=e+a;const h=a>0?1:-1;let d=this.get_ignored_region(o,h);for(;d.length;)o+=this.config.column_width*h,d=this.get_ignored_region(o,h),d.length||(o-=this.config.column_width*h);return o-e}get_ignored_region(t,e=1){return e===1?this.config.ignored_positions.filter(i=>t>i&&t<=i+this.config.column_width):this.config.ignored_positions.filter(i=>t>=i&&t<i+this.config.column_width)}unselect_all(){this.popup&&this.popup.parent.classList.add("hide"),this.$container.querySelectorAll(".date-range-highlight").forEach(t=>t.classList.add("hide"))}view_is(t){return typeof t=="string"?this.config.view_mode.name===t:Array.isArray(t)?t.some(view_is):this.config.view_mode.name===t.name}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 O(this.$popup_wrapper,this.options.popup,this)),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,i,s,n,a,o,h,d,_;this.$svg.innerHTML="",(e=(t=this.$header)==null?void 0:t.remove)==null||e.call(t),(s=(i=this.$side_header)==null?void 0:i.remove)==null||s.call(i),(a=(n=this.$current_highlight)==null?void 0:n.remove)==null||a.call(n),(h=(o=this.$extras)==null?void 0:o.remove)==null||h.call(o),(_=(d=this.popup)==null?void 0:d.hide)==null||_.call(d)}}X.VIEW_MODE={HOUR:m[0],QUARTER_DAY:m[1],HALF_DAY:m[2],DAY:m[3],WEEK:m[4],MONTH:m[5],YEAR:m[6]};function z(r){return r.name+"_"+Math.random().toString(36).slice(2,12)}function D(r){return r.replaceAll(" ","_").replaceAll(":","_").replaceAll(".","_")}return X});
|
|
29
|
+
</pattern>`;for(let i=new Date(this.gantt_start);i<=this.gantt_end;i.setDate(i.getDate()+1)){if(!this.config.ignored_dates.find(n=>n.getTime()==i.getTime())&&(!this.config.ignored_function||!this.config.ignored_function(i)))continue;let s=c.convert_scales(c.diff(i,this.gantt_start)+"d",this.config.unit)/this.config.step;this.config.ignored_positions.push(s*this.config.column_width),f("rect",{x:s*this.config.column_width,y:this.config.header_height,width:this.config.column_width,height:t,class:"ignored-bar",style:"fill: url(#diagonalHatch);",append_to:this.$svg})}this.highlight_current(this.config.view_mode)}create_el({left:t,top:e,width:i,height:s,id:n,classes:o,append_to:a,type:h}){let d=document.createElement(h||"div");for(let _ of o.split(" "))d.classList.add(_);return d.style.top=e+"px",d.style.left=t+"px",n&&(d.id=n),i&&(d.style.width=i+"px"),s&&(d.style.height=s+"px"),a&&a.appendChild(d),d}make_dates(){this.get_dates_to_draw().forEach((t,e)=>{if(t.lower_text){let i=this.create_el({left:t.x,top:t.lower_y,classes:"lower-text date_"+D(t.formatted_date),append_to:this.$lower_header});i.innerText=t.lower_text}if(t.upper_text){let i=this.create_el({left:t.x,top:t.upper_y,classes:"upper-text",append_to:this.$upper_header});i.innerText=t.upper_text}}),this.upperTexts=Array.from(this.$container.querySelectorAll(".upper-text"))}get_dates_to_draw(){let t=null;return this.dates.map((i,s)=>{const n=this.get_date_info(i,t,s);return t=n,n})}get_date_info(t,e){let i=e?e.date:null;this.config.column_width;const s=e?e.x+e.column_width:0;let n=this.config.view_mode.upper_text,o=this.config.view_mode.lower_text;return n?typeof n=="string"&&(this.config.view_mode.upper_text=a=>c.format(a,n,this.options.language)):this.config.view_mode.upper_text=()=>"",o?typeof o=="string"&&(this.config.view_mode.lower_text=a=>c.format(a,o,this.options.language)):this.config.view_mode.lower_text=()=>"",{date:t,formatted_date:D(c.format(t,this.config.date_format,this.options.language)),column_width:this.config.column_width,x:s,upper_text:this.config.view_mode.upper_text(t,i,this.options.language),lower_text:this.config.view_mode.lower_text(t,i,this.options.language),upper_y:17,lower_y:this.options.upper_header_height+5}}make_bars(){this.bars=this.tasks.map(t=>{const e=new q(this,t);return this.layers.bar.appendChild(e.group),e})}make_arrows(){this.arrows=[],this.options.critical_path&&this.calculate_critical_path();for(let t of this.tasks){let e=[];e=t.dependencies.map(i=>{const s=this.get_task(i);if(!s)return;const n=new C(this,this.bars[s._index],this.bars[t._index]);return this.layers.arrow.appendChild(n.element),n}).filter(Boolean),this.arrows=this.arrows.concat(e)}}calculate_critical_path(){this.tasks.forEach(n=>n._is_critical=!1);const t={};this.tasks.forEach(n=>{t[n.id]={es:0,ef:0,ls:0,lf:0}});const e=n=>{if(t[n.id].ef>0)return t[n.id];let o=0;n.dependencies&&n.dependencies.length>0&&n.dependencies.forEach(h=>{const d=this.get_task(h);if(d){const _=e(d);o=Math.max(o,_.ef)}}),t[n.id].es=o;const a=c.diff(n._end,n._start,"hour")/24;return t[n.id].ef=o+a,t[n.id]};this.tasks.forEach(n=>e(n));const i=Math.max(...Object.values(t).map(n=>n.ef)),s=n=>{if(t[n.id].ls>0||t[n.id].lf>0)return t[n.id];const o=this.tasks.filter(d=>d.dependencies&&d.dependencies.includes(n.id));let a=i;o.length>0&&o.forEach(d=>{const _=s(d);a=Math.min(a,_.ls)});const h=c.diff(n._end,n._start,"hour")/24;return t[n.id].lf=a,t[n.id].ls=a-h,t[n.id]};this.tasks.forEach(n=>s(n)),this.tasks.forEach(n=>{const o=t[n.id],a=o.ls-o.es;n._is_critical=Math.abs(a)<.01})}update_arrow_critical_path(){this.arrows.forEach(t=>{t.from_task.task._is_critical===!0&&t.to_task.task._is_critical===!0?t.element.classList.add("arrow-critical"):t.element.classList.remove("arrow-critical")})}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_dimensions(){const{width:t}=this.$svg.getBoundingClientRect(),e=this.$svg.querySelector(".grid .grid-row")?this.$svg.querySelector(".grid .grid-row").getAttribute("width"):0;t<e&&this.$svg.setAttribute("width",e)}set_scroll_position(t){if(this.options.infinite_padding&&(!t||t==="start")){let[o,...a]=this.get_start_end_positions();this.$container.scrollLeft=o;return}if(!t||t==="start")t=this.gantt_start;else if(t==="end")t=this.gantt_end;else{if(t==="today")return this.scroll_current();typeof t=="string"&&(t=c.parse(t))}const i=c.diff(t,this.gantt_start,this.config.unit)/this.config.step*this.config.column_width;this.$container.scrollTo({left:i-this.config.column_width/6,behavior:"smooth"}),this.$current&&this.$current.classList.remove("current-upper"),this.current_date=c.add(this.gantt_start,this.$container.scrollLeft/this.config.column_width,this.config.unit);let s=this.config.view_mode.upper_text(this.current_date,null,this.options.language),n=this.upperTexts.find(o=>o.textContent===s);this.current_date=c.add(this.gantt_start,(this.$container.scrollLeft+n.clientWidth)/this.config.column_width,this.config.unit),s=this.config.view_mode.upper_text(this.current_date,null,this.options.language),n=this.upperTexts.find(o=>o.textContent===s),n.classList.add("current-upper"),this.$current=n}scroll_current(){let t=this.get_closest_date();t&&this.set_scroll_position(t[0])}get_closest_date(){let t=new Date;if(t<this.gantt_start||t>this.gantt_end)return null;let e=new Date,i=this.$container.querySelector(".date_"+D(c.format(e,this.config.date_format,this.options.language))),s=0;for(;!i&&s<this.config.step;)e=c.add(e,-1,this.config.unit),i=this.$container.querySelector(".date_"+D(c.format(e,this.config.date_format,this.options.language))),s++;return[new Date(c.format(e,this.config.date_format,this.options.language)+" "),i]}bind_grid_click(){u.on(this.$container,"click",".grid-row, .grid-header, .ignored-bar, .holiday-highlight",()=>{this.unselect_all(),this.hide_popup()})}bind_holiday_labels(){const t=this.$container.querySelectorAll(".holiday-highlight");for(let e of t){const i=this.$container.querySelector(".label_"+e.classList[1]);if(!i)continue;let s;e.onmouseenter=n=>{s=setTimeout(()=>{i.classList.add("show"),i.style.left=(n.offsetX||n.layerX)+"px",i.style.top=(n.offsetY||n.layerY)+"px"},300)},e.onmouseleave=n=>{clearTimeout(s),i.classList.remove("show")}}}get_start_end_positions(){if(!this.bars.length)return[0,0,0];let{x:t,width:e}=this.bars[0].group.getBBox(),i=t,s=t,n=t+e;return Array.prototype.forEach.call(this.bars,function({group:o},a){let{x:h,width:d}=o.getBBox();h<i&&(i=h),h>s&&(s=h),h+d>n&&(n=h+d)}),[i,s,n]}bind_bar_events(){let t=!1,e=0,i=0,s=!1,n=!1,o=null,a=[];this.bar_being_dragged=null;const h=()=>t||s||n;this.$svg.onclick=_=>{_.target.classList.contains("grid-row")&&this.unselect_all()};let d=0;if(u.on(this.$svg,"mousemove",".bar-wrapper, .handle",_=>{this.bar_being_dragged===!1&&Math.abs((_.offsetX||_.layerX)-d)>10&&(this.bar_being_dragged=!0)}),u.on(this.$svg,"mousedown",".bar-wrapper, .handle",(_,l)=>{const p=u.closest(".bar-wrapper",l);l.classList.contains("left")?(s=!0,l.classList.add("visible")):l.classList.contains("right")?(n=!0,l.classList.add("visible")):l.classList.contains("bar-wrapper")&&(t=!0),this.popup&&this.popup.hide(),e=_.offsetX||_.layerX,o=p.getAttribute("data-id");const $=this.get_bar(o).task.dependencies_type||this.options.dependencies_type;let m;this.options.move_dependencies&&$==="fixed"?m=[o,...this.get_all_dependent_tasks(o)]:m=[o],a=m.map(k=>this.get_bar(k)),this.bar_being_dragged=!1,d=e,a.forEach(k=>{const b=k.$bar;b.ox=b.getX(),b.oy=b.getY(),b.owidth=b.getWidth(),b.finaldx=0})}),this.options.infinite_padding){let _=!1;u.on(this.$container,"mousewheel",l=>{let p=this.$container.scrollWidth/2;if(!_&&l.currentTarget.scrollLeft<=p){let g=l.currentTarget.scrollLeft;_=!0,this.gantt_start=c.add(this.gantt_start,-this.config.extend_by_units,this.config.unit),this.setup_date_values(),this.render(),l.currentTarget.scrollLeft=g+this.config.column_width*this.config.extend_by_units,setTimeout(()=>_=!1,300)}if(!_&&l.currentTarget.scrollWidth-(l.currentTarget.scrollLeft+l.currentTarget.clientWidth)<=p){let g=l.currentTarget.scrollLeft;_=!0,this.gantt_end=c.add(this.gantt_end,this.config.extend_by_units,this.config.unit),this.setup_date_values(),this.render(),l.currentTarget.scrollLeft=g,setTimeout(()=>_=!1,300)}})}u.on(this.$container,"scroll",_=>{let l=[];const p=this.bars.map(({group:y})=>y.getAttribute("data-id"));let g;i&&(g=_.currentTarget.scrollLeft-i),this.current_date=c.add(this.gantt_start,_.currentTarget.scrollLeft/this.config.column_width*this.config.step,this.config.unit);let $=this.config.view_mode.upper_text(this.current_date,null,this.options.language),m=this.upperTexts.find(y=>y.textContent===$);this.current_date=c.add(this.gantt_start,(_.currentTarget.scrollLeft+m.clientWidth)/this.config.column_width*this.config.step,this.config.unit),$=this.config.view_mode.upper_text(this.current_date,null,this.options.language),m=this.upperTexts.find(y=>y.textContent===$),m!==this.$current&&(this.$current&&this.$current.classList.remove("current-upper"),m.classList.add("current-upper"),this.$current=m),i=_.currentTarget.scrollLeft;let[k,b,B]=this.get_start_end_positions();i>B+100?(this.$adjust.innerHTML="←",this.$adjust.classList.remove("hide"),this.$adjust.onclick=()=>{this.$container.scrollTo({left:b,behavior:"smooth"})}):i+_.currentTarget.offsetWidth<k-100?(this.$adjust.innerHTML="→",this.$adjust.classList.remove("hide"),this.$adjust.onclick=()=>{this.$container.scrollTo({left:k,behavior:"smooth"})}):this.$adjust.classList.add("hide"),g&&(l=p.map(y=>this.get_bar(y)),this.options.auto_move_label&&l.forEach(y=>{y.update_label_position_on_horizontal_scroll({x:g,sx:_.currentTarget.scrollLeft})}))}),u.on(this.$svg,"mousemove",_=>{if(!h())return;const l=(_.offsetX||_.layerX)-e;a.forEach(p=>{const g=p.$bar;g.finaldx=this.get_snap_position(l,g.ox),this.hide_popup(),s?o===p.task.id?p.update_bar_position({x:g.ox+g.finaldx,width:g.owidth-g.finaldx}):p.update_bar_position({x:g.ox+g.finaldx}):n?o===p.task.id&&p.update_bar_position({width:g.owidth+g.finaldx}):t&&!this.options.readonly&&!this.options.readonly_dates&&p.update_bar_position({x:g.ox+g.finaldx})})}),document.addEventListener("mouseup",()=>{var _,l,p;t=!1,s=!1,n=!1,(p=(l=(_=this.$container.querySelector(".visible"))==null?void 0:_.classList)==null?void 0:l.remove)==null||p.call(l,"visible")}),u.on(this.$svg,"mouseup",_=>{this.bar_being_dragged=null;const l=[];a.forEach(g=>{g.$bar.finaldx&&(g.date_changed(),g.compute_progress(),g.set_action_completed(),l.push({task:g.task,start:g.task._start,end:c.add(g.task._end,-1,"second")}))});const p=this.get_bar(o);if(p&&p.$bar.finaldx){const g=this.update_dependent_tasks_by_type(p);l.push(...g)}this.options.critical_path&&a.some(g=>g.$bar.finaldx)&&(this.calculate_critical_path(),this.update_arrow_critical_path()),l.length>0&&l.forEach(({task:g,start:$,end:m})=>{this.trigger_event("after_date_change",[g,$,m])})}),this.bind_bar_progress()}bind_bar_progress(){let t=0,e=null,i=null,s=null,n=null;u.on(this.$svg,"mousedown",".handle.progress",(a,h)=>{e=!0,t=a.offsetX||a.layerX;const _=u.closest(".bar-wrapper",h).getAttribute("data-id");i=this.get_bar(_),s=i.$bar_progress,n=i.$bar,s.finaldx=0,s.owidth=s.getWidth(),s.min_dx=-s.owidth,s.max_dx=n.getWidth()-s.getWidth()});const o=this.config.ignored_positions.map(a=>[a,a+this.config.column_width]);u.on(this.$svg,"mousemove",a=>{if(!e)return;let h=a.offsetX||a.layerX;if(h>t){let l=o.find(([p,g])=>h>=p&&h<g);for(;l;)h=l[1],l=o.find(([p,g])=>h>=p&&h<g)}else{let l=o.find(([p,g])=>h>p&&h<=g);for(;l;)h=l[0],l=o.find(([p,g])=>h>p&&h<=g)}let _=h-t;console.log(s),_>s.max_dx&&(_=s.max_dx),_<s.min_dx&&(_=s.min_dx),s.setAttribute("width",s.owidth+_),u.attr(i.$handle_progress,"cx",s.getEndX()),s.finaldx=_}),u.on(this.$svg,"mouseup",()=>{e=!1,s&&s.finaldx&&(s.finaldx=0,i.progress_changed(),i.set_action_completed(),i=null,s=null,n=null)})}get_all_dependent_tasks(t){let e=[],i=[t];for(;i.length;){const s=i.reduce((n,o)=>(n=n.concat(this.dependency_map[o]),n),[]);e=e.concat(s),i=s.filter(n=>!i.includes(n))}return e.filter(Boolean)}update_dependent_tasks_by_type(t){const e=t.task.dependencies_type||this.options.dependencies_type,i=[];return e==="fixed"||(this.dependency_map[t.task.id]||[]).forEach(n=>{const o=this.get_bar(n);if(!o)return;const a=o.task,h=a.dependencies_type||this.options.dependencies_type;let d,_;const l=c.diff(a._end,a._start,"hour");let p=!1;switch(h){case"finish-to-start":t.task._end>a._start&&(d=new Date(t.task._end),_=c.add(d,l,"hour"),p=!0);break;case"start-to-start":t.task._start>a._start&&(d=new Date(t.task._start),_=c.add(d,l,"hour"),p=!0);break;case"finish-to-finish":t.task._end>a._end&&(_=new Date(t.task._end),d=c.add(_,-l,"hour"),p=!0);break;case"start-to-finish":t.task._start>a._end&&(_=new Date(t.task._start),d=c.add(_,-l,"hour"),p=!0);break;default:return}if(!p)return;a._start=d,a._end=_,o.compute_x(),o.compute_duration(),o.update_bar_position({x:o.x,width:o.width}),this.trigger_event("date_change",[a,d,c.add(_,-1,"second")]),i.push({task:a,start:d,end:c.add(_,-1,"second")});const g=this.update_dependent_tasks_by_type(o);i.push(...g)}),i}get_snap_position(t,e){let i=1;const s=this.options.snap_at||this.config.view_mode.snap_at||"1d";if(s!=="unit"){const{duration:_,scale:l}=c.parse_duration(s);i=c.convert_scales(this.config.view_mode.step,l)/_}const n=t%(this.config.column_width/i);let o=t-n+(n<this.config.column_width/i*2?0:this.config.column_width/i),a=e+o;const h=o>0?1:-1;let d=this.get_ignored_region(a,h);for(;d.length;)a+=this.config.column_width*h,d=this.get_ignored_region(a,h),d.length||(a-=this.config.column_width*h);return a-e}get_ignored_region(t,e=1){return e===1?this.config.ignored_positions.filter(i=>t>i&&t<=i+this.config.column_width):this.config.ignored_positions.filter(i=>t>=i&&t<i+this.config.column_width)}unselect_all(){this.popup&&this.popup.parent.classList.add("hide"),this.$container.querySelectorAll(".date-range-highlight").forEach(t=>t.classList.add("hide"))}view_is(t){return typeof t=="string"?this.config.view_mode.name===t:Array.isArray(t)?t.some(view_is):this.config.view_mode.name===t.name}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 F(this.$popup_wrapper,this.options.popup,this)),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,i,s,n,o,a,h,d,_;this.$svg.innerHTML="",(e=(t=this.$header)==null?void 0:t.remove)==null||e.call(t),(s=(i=this.$side_header)==null?void 0:i.remove)==null||s.call(i),(o=(n=this.$current_highlight)==null?void 0:n.remove)==null||o.call(n),(h=(a=this.$extras)==null?void 0:a.remove)==null||h.call(a),(_=(d=this.popup)==null?void 0:d.hide)==null||_.call(d)}}X.VIEW_MODE={HOUR:w[0],QUARTER_DAY:w[1],HALF_DAY:w[2],DAY:w[3],WEEK:w[4],MONTH:w[5],YEAR:w[6]};function j(r){return r.name+"_"+Math.random().toString(36).slice(2,12)}function D(r){return r.replaceAll(" ","_").replaceAll(":","_").replaceAll(".","_")}return X});
|
package/package.json
CHANGED
package/src/bar.js
CHANGED
|
@@ -28,6 +28,10 @@ export default class Bar {
|
|
|
28
28
|
this.gantt = gantt;
|
|
29
29
|
this.task = task;
|
|
30
30
|
this.name = this.name || '';
|
|
31
|
+
this.is_dragging = false;
|
|
32
|
+
this.is_hovering_bar = false;
|
|
33
|
+
this.is_hovering_icon = false;
|
|
34
|
+
this.add_icon_hide_timeout = null;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
prepare_wrappers() {
|
|
@@ -106,6 +110,10 @@ export default class Bar {
|
|
|
106
110
|
if (this.task.thumbnail) {
|
|
107
111
|
this.draw_thumbnail();
|
|
108
112
|
}
|
|
113
|
+
|
|
114
|
+
if (this.gantt.options.task_add_icon_position) {
|
|
115
|
+
this.draw_add_task_icon();
|
|
116
|
+
}
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
draw_bar() {
|
|
@@ -277,6 +285,99 @@ export default class Bar {
|
|
|
277
285
|
});
|
|
278
286
|
}
|
|
279
287
|
|
|
288
|
+
draw_add_task_icon() {
|
|
289
|
+
const icon_size = 20;
|
|
290
|
+
const icon_padding = 5;
|
|
291
|
+
this.icon_size = icon_size;
|
|
292
|
+
this.icon_padding = icon_padding;
|
|
293
|
+
let icon_x;
|
|
294
|
+
|
|
295
|
+
if (this.gantt.options.task_add_icon_position === 'before') {
|
|
296
|
+
icon_x = this.x - icon_size - icon_padding;
|
|
297
|
+
} else if (this.gantt.options.task_add_icon_position === 'after') {
|
|
298
|
+
icon_x = this.x + this.$bar.getWidth() + icon_padding;
|
|
299
|
+
} else {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const icon_y = this.y + (this.height - icon_size) / 2;
|
|
304
|
+
|
|
305
|
+
// Create icon group
|
|
306
|
+
this.$add_icon_group = createSVG('g', {
|
|
307
|
+
class: 'add-task-icon hide',
|
|
308
|
+
append_to: this.handle_group,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Create circle background
|
|
312
|
+
this.$add_icon_circle = createSVG('circle', {
|
|
313
|
+
cx: icon_x + icon_size / 2,
|
|
314
|
+
cy: icon_y + icon_size / 2,
|
|
315
|
+
r: icon_size / 2,
|
|
316
|
+
class: 'add-task-icon-bg',
|
|
317
|
+
append_to: this.$add_icon_group,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Create plus sign (vertical line)
|
|
321
|
+
this.$add_icon_vertical = createSVG('line', {
|
|
322
|
+
x1: icon_x + icon_size / 2,
|
|
323
|
+
y1: icon_y + 5,
|
|
324
|
+
x2: icon_x + icon_size / 2,
|
|
325
|
+
y2: icon_y + icon_size - 5,
|
|
326
|
+
class: 'add-task-icon-plus',
|
|
327
|
+
append_to: this.$add_icon_group,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Create plus sign (horizontal line)
|
|
331
|
+
this.$add_icon_horizontal = createSVG('line', {
|
|
332
|
+
x1: icon_x + 5,
|
|
333
|
+
y1: icon_y + icon_size / 2,
|
|
334
|
+
x2: icon_x + icon_size - 5,
|
|
335
|
+
y2: icon_y + icon_size / 2,
|
|
336
|
+
class: 'add-task-icon-plus',
|
|
337
|
+
append_to: this.$add_icon_group,
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Prevent mousedown from starting drag process
|
|
341
|
+
$.on(this.$add_icon_group, 'mousedown', (e) => {
|
|
342
|
+
e.stopPropagation();
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Prevent mouseup from triggering popup
|
|
346
|
+
$.on(this.$add_icon_group, 'mouseup', (e) => {
|
|
347
|
+
e.stopPropagation();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Add click event handler
|
|
351
|
+
$.on(this.$add_icon_group, 'click', (e) => {
|
|
352
|
+
e.stopPropagation();
|
|
353
|
+
this.gantt.trigger_event('task_add', [this.task]);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Add hover effect
|
|
357
|
+
$.on(this.$add_icon_group, 'mouseenter', (e) => {
|
|
358
|
+
this.is_hovering_icon = true;
|
|
359
|
+
// Cancel any pending hide timeout
|
|
360
|
+
if (this.add_icon_hide_timeout) {
|
|
361
|
+
clearTimeout(this.add_icon_hide_timeout);
|
|
362
|
+
this.add_icon_hide_timeout = null;
|
|
363
|
+
}
|
|
364
|
+
this.$add_icon_group.classList.add('active');
|
|
365
|
+
// Keep icon visible when hovering over it
|
|
366
|
+
this.$add_icon_group.classList.remove('hide');
|
|
367
|
+
// Prevent event from bubbling to trigger hover events
|
|
368
|
+
e.stopPropagation();
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
$.on(this.$add_icon_group, 'mouseleave', () => {
|
|
372
|
+
this.is_hovering_icon = false;
|
|
373
|
+
this.$add_icon_group.classList.remove('active');
|
|
374
|
+
// Only hide if not hovering over the bar
|
|
375
|
+
if (!this.is_hovering_bar) {
|
|
376
|
+
this.$add_icon_group.classList.add('hide');
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
280
381
|
draw_resize_handles() {
|
|
281
382
|
if (this.invalid || this.gantt.options.readonly) return;
|
|
282
383
|
|
|
@@ -374,6 +475,19 @@ export default class Bar {
|
|
|
374
475
|
.querySelector(`.highlight-${task_id}`)
|
|
375
476
|
.classList.remove('hide');
|
|
376
477
|
}, 200);
|
|
478
|
+
|
|
479
|
+
// Show add task icon on hover only if not dragging
|
|
480
|
+
if (this.$add_icon_group) {
|
|
481
|
+
this.is_hovering_bar = true;
|
|
482
|
+
if (this.add_icon_hide_timeout) {
|
|
483
|
+
clearTimeout(this.add_icon_hide_timeout);
|
|
484
|
+
this.add_icon_hide_timeout = null;
|
|
485
|
+
}
|
|
486
|
+
// Only show if not currently dragging
|
|
487
|
+
if (!this.is_dragging) {
|
|
488
|
+
this.$add_icon_group.classList.remove('hide');
|
|
489
|
+
}
|
|
490
|
+
}
|
|
377
491
|
});
|
|
378
492
|
$.on(this.group, 'mouseleave', () => {
|
|
379
493
|
clearTimeout(timeout);
|
|
@@ -382,9 +496,33 @@ export default class Bar {
|
|
|
382
496
|
this.gantt.$container
|
|
383
497
|
.querySelector(`.highlight-${task_id}`)
|
|
384
498
|
.classList.add('hide');
|
|
499
|
+
|
|
500
|
+
// Hide add task icon on mouse leave with delay
|
|
501
|
+
if (this.$add_icon_group) {
|
|
502
|
+
this.is_hovering_bar = false;
|
|
503
|
+
// Clear any existing timeout
|
|
504
|
+
if (this.add_icon_hide_timeout) {
|
|
505
|
+
clearTimeout(this.add_icon_hide_timeout);
|
|
506
|
+
}
|
|
507
|
+
// Add delay to allow mouse to reach the icon
|
|
508
|
+
this.add_icon_hide_timeout = setTimeout(() => {
|
|
509
|
+
// Only hide if not hovering over icon
|
|
510
|
+
if (!this.is_hovering_icon) {
|
|
511
|
+
this.$add_icon_group.classList.add('hide');
|
|
512
|
+
}
|
|
513
|
+
}, 200);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
$.on(this.group, 'mousedown', () => {
|
|
518
|
+
// Mark as dragging and hide add icon
|
|
519
|
+
this.is_dragging = true;
|
|
520
|
+
this.hide_add_icon();
|
|
385
521
|
});
|
|
386
522
|
|
|
387
523
|
$.on(this.group, 'click', () => {
|
|
524
|
+
// Don't trigger click if we just finished dragging
|
|
525
|
+
if (this.action_completed || this.gantt.bar_being_dragged) return;
|
|
388
526
|
this.gantt.trigger_event('click', [this.task]);
|
|
389
527
|
});
|
|
390
528
|
|
|
@@ -442,6 +580,7 @@ export default class Bar {
|
|
|
442
580
|
|
|
443
581
|
this.update_label_position();
|
|
444
582
|
this.update_handle_position();
|
|
583
|
+
this.update_add_icon_position();
|
|
445
584
|
this.date_changed();
|
|
446
585
|
this.compute_duration();
|
|
447
586
|
|
|
@@ -590,6 +729,12 @@ export default class Bar {
|
|
|
590
729
|
set_action_completed() {
|
|
591
730
|
this.action_completed = true;
|
|
592
731
|
setTimeout(() => (this.action_completed = false), 1000);
|
|
732
|
+
// Mark dragging as complete
|
|
733
|
+
this.is_dragging = false;
|
|
734
|
+
// Show add icon if hovering
|
|
735
|
+
if (this.$add_icon_group && this.is_hovering_bar) {
|
|
736
|
+
this.$add_icon_group.classList.remove('hide');
|
|
737
|
+
}
|
|
593
738
|
}
|
|
594
739
|
|
|
595
740
|
compute_start_end_date() {
|
|
@@ -799,6 +944,45 @@ export default class Bar {
|
|
|
799
944
|
handle && handle.setAttribute('cx', this.$bar_progress.getEndX());
|
|
800
945
|
}
|
|
801
946
|
|
|
947
|
+
update_add_icon_position() {
|
|
948
|
+
if (!this.$add_icon_group) return;
|
|
949
|
+
|
|
950
|
+
const icon_size = this.icon_size;
|
|
951
|
+
const icon_padding = this.icon_padding;
|
|
952
|
+
let icon_x;
|
|
953
|
+
|
|
954
|
+
if (this.gantt.options.task_add_icon_position === 'before') {
|
|
955
|
+
icon_x = this.x - icon_size - icon_padding;
|
|
956
|
+
} else if (this.gantt.options.task_add_icon_position === 'after') {
|
|
957
|
+
icon_x = this.x + this.$bar.getWidth() + icon_padding;
|
|
958
|
+
} else {
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
const icon_y = this.y + (this.height - icon_size) / 2;
|
|
963
|
+
|
|
964
|
+
// Update circle position
|
|
965
|
+
this.$add_icon_circle.setAttribute('cx', icon_x + icon_size / 2);
|
|
966
|
+
this.$add_icon_circle.setAttribute('cy', icon_y + icon_size / 2);
|
|
967
|
+
|
|
968
|
+
// Update vertical line position
|
|
969
|
+
this.$add_icon_vertical.setAttribute('x1', icon_x + icon_size / 2);
|
|
970
|
+
this.$add_icon_vertical.setAttribute('y1', icon_y + 5);
|
|
971
|
+
this.$add_icon_vertical.setAttribute('x2', icon_x + icon_size / 2);
|
|
972
|
+
this.$add_icon_vertical.setAttribute('y2', icon_y + icon_size - 5);
|
|
973
|
+
|
|
974
|
+
// Update horizontal line position
|
|
975
|
+
this.$add_icon_horizontal.setAttribute('x1', icon_x + 5);
|
|
976
|
+
this.$add_icon_horizontal.setAttribute('y1', icon_y + icon_size / 2);
|
|
977
|
+
this.$add_icon_horizontal.setAttribute('x2', icon_x + icon_size - 5);
|
|
978
|
+
this.$add_icon_horizontal.setAttribute('y2', icon_y + icon_size / 2);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
hide_add_icon() {
|
|
982
|
+
if (!this.$add_icon_group) return;
|
|
983
|
+
this.$add_icon_group.classList.add('hide');
|
|
984
|
+
}
|
|
985
|
+
|
|
802
986
|
update_arrow_position() {
|
|
803
987
|
this.arrows = this.arrows || [];
|
|
804
988
|
for (let arrow of this.arrows) {
|