|
|
|
@ -1,3 +1,7 @@ |
|
|
|
|
import * as moment from 'moment'; |
|
|
|
|
import {openprojectModule} from '../../../../angular-modules'; |
|
|
|
|
import {TimelineZoomLevel} from '../../../api/api-v3/hal-resources/query-resource.service'; |
|
|
|
|
import {WorkPackageTimelineTableController} from '../container/wp-timeline-container.directive'; |
|
|
|
|
// -- copyright
|
|
|
|
|
// OpenProject is a project management system.
|
|
|
|
|
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
|
|
|
@ -25,16 +29,8 @@ |
|
|
|
|
//
|
|
|
|
|
// See doc/COPYRIGHT.rdoc for more details.
|
|
|
|
|
// ++
|
|
|
|
|
import { |
|
|
|
|
TimelineViewParameters, |
|
|
|
|
timelineElementCssClass, |
|
|
|
|
calculatePositionValueForDayCount |
|
|
|
|
} from "../wp-timeline"; |
|
|
|
|
import {WorkPackageTimelineTableController} from "../container/wp-timeline-container.directive"; |
|
|
|
|
import * as moment from 'moment'; |
|
|
|
|
import {calculatePositionValueForDayCount, getTimeSlicesForHeader, TimelineViewParameters} from '../wp-timeline'; |
|
|
|
|
import Moment = moment.Moment; |
|
|
|
|
import {openprojectModule} from "../../../../angular-modules"; |
|
|
|
|
import {TimelineZoomLevel} from "../../../api/api-v3/hal-resources/query-resource.service"; |
|
|
|
|
|
|
|
|
|
export const timelineHeaderCSSClass = 'wp-timeline--header-element'; |
|
|
|
|
|
|
|
|
@ -83,133 +79,132 @@ export class WorkPackageTimelineHeaderController { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private renderLabelsDays(vp:TimelineViewParameters) { |
|
|
|
|
this.renderTimeSlices(vp, "month", 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("MMM YYYY"); |
|
|
|
|
this.renderTimeSlices(vp, 'month', 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('MMM YYYY'); |
|
|
|
|
cell.classList.add('wp-timeline--header-top-bold-element'); |
|
|
|
|
cell.style.height = "13px"; |
|
|
|
|
cell.style.height = '13px'; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "week", 13, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("ww"); |
|
|
|
|
this.renderTimeSlices(vp, 'week', 13, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('ww'); |
|
|
|
|
cell.classList.add('-top-border'); |
|
|
|
|
cell.style.height = '32px'; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "day", 23, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("D"); |
|
|
|
|
this.renderTimeSlices(vp, 'day', 23, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('D'); |
|
|
|
|
cell.classList.add('-top-border'); |
|
|
|
|
cell.style.height = '22px'; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "day", 33, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("dd"); |
|
|
|
|
this.renderTimeSlices(vp, 'day', 33, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('dd'); |
|
|
|
|
cell.classList.add('wp-timeline--header-day-element'); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private renderLabelsWeeks(vp:TimelineViewParameters) { |
|
|
|
|
this.renderTimeSlices(vp, "month", 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("MMM YYYY"); |
|
|
|
|
this.renderTimeSlices(vp, 'month', 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('MMM YYYY'); |
|
|
|
|
cell.classList.add('wp-timeline--header-top-bold-element'); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "week", 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("ww"); |
|
|
|
|
this.renderTimeSlices(vp, 'week', 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('ww'); |
|
|
|
|
cell.classList.add('-top-border'); |
|
|
|
|
cell.style.height = '22px'; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "day", 25, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("D"); |
|
|
|
|
this.renderTimeSlices(vp, 'day', 25, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('D'); |
|
|
|
|
cell.classList.add('wp-timeline--header-middle-element'); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private renderLabelsMonths(vp:TimelineViewParameters) { |
|
|
|
|
this.renderTimeSlices(vp, "year", 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("YYYY"); |
|
|
|
|
this.renderTimeSlices(vp, 'year', 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('YYYY'); |
|
|
|
|
cell.classList.add('wp-timeline--header-top-bold-element'); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "month", 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("MMM"); |
|
|
|
|
this.renderTimeSlices(vp, 'month', 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('MMM'); |
|
|
|
|
cell.classList.add('-top-border'); |
|
|
|
|
cell.style.height = '30px'; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "week", 25, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("ww"); |
|
|
|
|
this.renderTimeSlices(vp, 'week', 25, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('ww'); |
|
|
|
|
cell.classList.add('wp-timeline--header-middle-element'); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private renderLabelsQuarters(vp:TimelineViewParameters) { |
|
|
|
|
this.renderTimeSlices(vp, "year", 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
this.renderTimeSlices(vp, 'year', 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.classList.add('wp-timeline--header-top-bold-element'); |
|
|
|
|
cell.innerHTML = start.format("YYYY"); |
|
|
|
|
cell.innerHTML = start.format('YYYY'); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "quarter", 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = "Q" + start.format("Q"); |
|
|
|
|
this.renderTimeSlices(vp, 'quarter', 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = 'Q' + start.format('Q'); |
|
|
|
|
cell.classList.add('-top-border'); |
|
|
|
|
cell.style.height = '30px'; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "month", 25, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("MMM"); |
|
|
|
|
this.renderTimeSlices(vp, 'month', 25, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('MMM'); |
|
|
|
|
cell.classList.add('wp-timeline--header-middle-element'); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private renderLabelsYears(vp:TimelineViewParameters) { |
|
|
|
|
this.renderTimeSlices(vp, "year", 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("YYYY"); |
|
|
|
|
this.renderTimeSlices(vp, 'year', 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('YYYY'); |
|
|
|
|
cell.classList.add('wp-timeline--header-top-bold-element'); |
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "quarter", 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = "Q" + start.format("Q"); |
|
|
|
|
this.renderTimeSlices(vp, 'quarter', 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = 'Q' + start.format('Q'); |
|
|
|
|
cell.classList.add('-top-border'); |
|
|
|
|
cell.style.height = '30px'; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderTimeSlices(vp, "month", 25, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format("M"); |
|
|
|
|
this.renderTimeSlices(vp, 'month', 25, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { |
|
|
|
|
cell.innerHTML = start.format('M'); |
|
|
|
|
cell.classList.add('wp-timeline--header-middle-element'); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
renderTimeSlices(vp:TimelineViewParameters, |
|
|
|
|
unit:moment.unitOfTime.DurationConstructor, |
|
|
|
|
marginTop:number, |
|
|
|
|
startView:Moment, |
|
|
|
|
endView:Moment, |
|
|
|
|
cellCallback:(start:Moment, cell:HTMLElement) => void) { |
|
|
|
|
|
|
|
|
|
const slices:[Moment, Moment][] = []; |
|
|
|
|
private renderTimeSlices(vp:TimelineViewParameters, |
|
|
|
|
unit:moment.unitOfTime.DurationConstructor, |
|
|
|
|
marginTop:number, |
|
|
|
|
startView:Moment, |
|
|
|
|
endView:Moment, |
|
|
|
|
cellCallback:(start:Moment, cell:HTMLElement) => void) { |
|
|
|
|
|
|
|
|
|
const time = startView.clone().startOf(unit); |
|
|
|
|
const end = endView.clone().endOf(unit); |
|
|
|
|
|
|
|
|
|
while (time.isBefore(end)) { |
|
|
|
|
const sliceStart = moment.max(time, startView).clone(); |
|
|
|
|
const sliceEnd = moment.min(time.clone().endOf(unit), endView).clone(); |
|
|
|
|
time.add(1, unit); |
|
|
|
|
slices.push([sliceStart, sliceEnd]); |
|
|
|
|
} |
|
|
|
|
const {inViewportAndBoundaries, rest} = getTimeSlicesForHeader(vp, unit, startView, endView); |
|
|
|
|
|
|
|
|
|
for (let [start, end] of slices) { |
|
|
|
|
for (let [start, end] of inViewportAndBoundaries) { |
|
|
|
|
const cell = this.addLabelCell(); |
|
|
|
|
cell.style.top = marginTop + "px"; |
|
|
|
|
cell.style.left = calculatePositionValueForDayCount(vp, start.diff(startView, "days")); |
|
|
|
|
cell.style.width = calculatePositionValueForDayCount(vp, end.diff(start, "days") + 1); |
|
|
|
|
cell.style.top = marginTop + 'px'; |
|
|
|
|
cell.style.left = calculatePositionValueForDayCount(vp, start.diff(startView, 'days')); |
|
|
|
|
cell.style.width = calculatePositionValueForDayCount(vp, end.diff(start, 'days') + 1); |
|
|
|
|
cellCallback(start, cell); |
|
|
|
|
} |
|
|
|
|
setTimeout(() => { |
|
|
|
|
for (let [start, end] of rest) { |
|
|
|
|
const cell = this.addLabelCell(); |
|
|
|
|
cell.style.top = marginTop + 'px'; |
|
|
|
|
cell.style.left = calculatePositionValueForDayCount(vp, start.diff(startView, 'days')); |
|
|
|
|
cell.style.width = calculatePositionValueForDayCount(vp, end.diff(start, 'days') + 1); |
|
|
|
|
cellCallback(start, cell); |
|
|
|
|
} |
|
|
|
|
}, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private addLabelCell():HTMLElement { |
|
|
|
|
const label = document.createElement("div"); |
|
|
|
|
const label = document.createElement('div'); |
|
|
|
|
label.className = timelineHeaderCSSClass; |
|
|
|
|
|
|
|
|
|
this.innerHeader.append(label); |
|
|
|
@ -217,7 +212,7 @@ export class WorkPackageTimelineHeaderController { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
openprojectModule.component("wpTimelineHeader", { |
|
|
|
|
openprojectModule.component('wpTimelineHeader', { |
|
|
|
|
templateUrl: '/components/wp-table/timeline/header/wp-timeline-header.html', |
|
|
|
|
controller: WorkPackageTimelineHeaderController, |
|
|
|
|
require: { |
|
|
|
|