diff --git a/app/assets/stylesheets/content/work_packages/timelines/_timelines.sass b/app/assets/stylesheets/content/work_packages/timelines/_timelines.sass index 39f87c333d..e4c44f8958 100644 --- a/app/assets/stylesheets/content/work_packages/timelines/_timelines.sass +++ b/app/assets/stylesheets/content/work_packages/timelines/_timelines.sass @@ -3,6 +3,8 @@ @import 'elements/_milestone' @import 'elements/_relation' +@import '_timelines_static_elements' + .wp-timeline--th min-width: 600px !important diff --git a/app/assets/stylesheets/content/work_packages/timelines/_timelines_static_elements.sass b/app/assets/stylesheets/content/work_packages/timelines/_timelines_static_elements.sass new file mode 100644 index 0000000000..073c280928 --- /dev/null +++ b/app/assets/stylesheets/content/work_packages/timelines/_timelines_static_elements.sass @@ -0,0 +1,8 @@ +.wp-timeline--static-element + z-index: 200 + +#wp-timeline-static-element-today-line + position: absolute + width: 2px + border-left: 2px dotted red + height: 100% diff --git a/frontend/app/components/wp-table/timeline/global-elements/timeline-static-element.ts b/frontend/app/components/wp-table/timeline/global-elements/timeline-static-element.ts new file mode 100644 index 0000000000..1633df0fed --- /dev/null +++ b/frontend/app/components/wp-table/timeline/global-elements/timeline-static-element.ts @@ -0,0 +1,28 @@ +import {TimelineViewParameters} from "../wp-timeline"; +export const timelineStaticElementCssClassname = "wp-timeline--static-element"; + +export abstract class TimelineStaticElement { + constructor() { + } + + /** + * Render the static element according to the current ViewParameters + * @param vp Current timeline view paraemters + * @returns {HTMLElement} The finished static element + */ + public render(vp:TimelineViewParameters):HTMLElement { + const elem = document.createElement("div"); + elem.id = this.identifier; + elem.classList.add(...this.classNames); + + return this.finishElement(elem, vp); + } + + protected abstract finishElement(elem:HTMLElement, vp:TimelineViewParameters):HTMLElement; + + public abstract get identifier():string; + + public get classNames():string[] { + return [timelineStaticElementCssClassname]; + } +} diff --git a/frontend/app/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts b/frontend/app/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts new file mode 100644 index 0000000000..42098af8af --- /dev/null +++ b/frontend/app/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts @@ -0,0 +1,98 @@ +// -- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See doc/COPYRIGHT.rdoc for more details. +// ++ +import { + timelineElementCssClass, + TimelineViewParameters, +} from "../wp-timeline"; +import {WorkPackageTimelineTableController} from "../container/wp-timeline-container.directive"; + +import {Observable} from 'rxjs'; +import * as moment from 'moment'; +import Moment = moment.Moment; +import {openprojectModule} from "../../../../angular-modules"; +import {States} from "../../../states.service"; +import {WorkPackageStates} from "../../../work-package-states.service"; +import { + RelationsStateValue, + WorkPackageRelationsService +} from "../../../wp-relations/wp-relations.service"; +import {scopeDestroyed$} from "../../../../helpers/angular-rx-utils"; +import {TimelineRelationElement} from "./timeline-relation-element"; +import {RelationResource} from "../../../api/api-v3/hal-resources/relation-resource.service"; +import {TimelineStaticElement, timelineStaticElementCssClassname} from "./timeline-static-element"; +import {timelineGlobalElementCssClassname} from "./wp-timeline-relations.directive"; +import {TodayLineElement} from "./wp-timeline.today-line"; + + +export class WorkPackageTableTimelineStaticElements { + + public wpTimeline:WorkPackageTimelineTableController; + + private container:ng.IAugmentedJQuery; + + private elements:TimelineStaticElement[]; + + constructor(public $element:ng.IAugmentedJQuery, + public $scope:ng.IScope, + public states:States, + public wpStates:WorkPackageStates, + public wpRelations:WorkPackageRelationsService) { + + this.elements = [ + new TodayLineElement() + ]; + } + + $onInit() { + this.container = this.$element.find('.wp-table-timeline--static-elements'); + this.wpTimeline.onRefreshRequested('static elements', (vp:TimelineViewParameters) => this.update(vp)); + } + + private update(vp:TimelineViewParameters) { + this.removeAllVisibleElements(); + this.renderElements(vp); + } + + private removeAllVisibleElements() { + jQuery('.' + timelineStaticElementCssClassname).remove(); + } + + private renderElements(vp:TimelineViewParameters) { + for (const e of this.elements) { + this.container[0].appendChild(e.render(vp)); + } + } +} + +openprojectModule.component("wpTimelineStaticElements", { + template: '
', + controller: WorkPackageTableTimelineStaticElements, + require: { + wpTimeline: '^wpTimelineContainer' + } +}); diff --git a/frontend/app/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts b/frontend/app/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts index 68a96aa990..91f4df9abf 100644 --- a/frontend/app/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts +++ b/frontend/app/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts @@ -28,15 +28,21 @@ import {calculatePositionValueForDayCount, TimelineViewParameters} from "../wp-timeline"; import * as moment from 'moment'; +import {TimelineStaticElement} from "./timeline-static-element"; -// Today Line -export function todayLine(viewParams: TimelineViewParameters, elem: HTMLElement) { - elem.style.width = "2px"; - elem.style.borderLeft = "2px dotted red"; - const offsetToday = viewParams.now.diff(viewParams.dateDisplayStart, "days"); - const dayProgress = moment().hour() / 24; - elem.style.left = calculatePositionValueForDayCount(viewParams, offsetToday + dayProgress); - elem.style.marginLeft = viewParams.scrollOffsetInPx + "px"; +export class TodayLineElement extends TimelineStaticElement { + + protected finishElement(elem:HTMLElement, vp:TimelineViewParameters):HTMLElement { + const offsetToday = vp.now.diff(vp.dateDisplayStart, "days"); + const dayProgress = moment().hour() / 24; + elem.style.left = calculatePositionValueForDayCount(vp, offsetToday + dayProgress); + + return elem; + } + + public get identifier():string { + return 'wp-timeline-static-element-today-line'; + } }