interval planner
This commit is contained in:
parent
e4b26961b0
commit
c728a5e096
@ -0,0 +1,36 @@
|
||||
<div class="container">
|
||||
<div class="time-head" #barContainer>
|
||||
@for(item of view.cellDescriptors; track item.title) {
|
||||
<div class="cell">
|
||||
<div>
|
||||
<div class="content">
|
||||
<div class="ticks">
|
||||
@for (item of view.tickDescriptors; track item.title) {
|
||||
<span class="tick" [style.width]="item.width">{{item.title}}</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@for (day of weekdays; track day) {
|
||||
<div class="weekday" [class.weekend]="day === 7 || day === 6" [class.sunday]="day === 7">{{formatDayName(day)}}
|
||||
</div>
|
||||
<div class="bar-row">
|
||||
<!-- Draw open periods -->
|
||||
@for (time of view.tickDescriptors; track time.title; let index = $index) {
|
||||
<div class="hour-separator" [style.width]="time.width" [style.left.px]="time.raw_width * index">
|
||||
</div>
|
||||
}
|
||||
@for (interval of intervalSet[day]; track interval) {
|
||||
<div class="interval" [style.width.px]="view.durationToWidth(interval.duration)"
|
||||
[style.left.px]="view.durationToWidth(interval.start)">
|
||||
<div class="resize-action" (mousedown)="resizing = interval" (mouseup)="resizing = null"></div>
|
||||
<div class="resize-action" (mousedown)="resizing = interval" (mouseup)="resizing = null"></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
104
src/app/gantt/interval-planning/interval-planning.component.scss
Normal file
104
src/app/gantt/interval-planning/interval-planning.component.scss
Normal file
@ -0,0 +1,104 @@
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: #ccc;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
|
||||
--bar-margin: 5px;
|
||||
--row-height: 1.5rem;
|
||||
|
||||
--header-height: 4rem;
|
||||
|
||||
--bg-color: rgb(247, 247, 247); //#fafafa;
|
||||
--separator-color: rgb(238, 238, 238);
|
||||
--bg-weekend-color: #e7dada;
|
||||
--bg-open-color: #fff;
|
||||
--bg-closed-color: rgb(208, 208, 208);
|
||||
|
||||
--bg-week-color-even: #98afc7;
|
||||
--bg-week-color-odd: #8391a1;
|
||||
|
||||
background-color: var(--bg-color);
|
||||
|
||||
border: solid thin black;
|
||||
box-sizing: border-box;
|
||||
|
||||
.time-head {
|
||||
grid-column: 2;
|
||||
|
||||
.ticks {
|
||||
display: flex;
|
||||
color: #000;
|
||||
|
||||
:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.tick {
|
||||
padding-left: 0.2rem;
|
||||
box-sizing: border-box;
|
||||
font-size: 0.7rem;
|
||||
border-right: solid thin #fff;
|
||||
border-color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.weekday {
|
||||
height: calc(var(--row-height) + 2 * var(--bar-margin));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
padding: 0 0.5rem;
|
||||
border-bottom: solid thin var(--separator-color);
|
||||
|
||||
&.weekend {
|
||||
background-color: var(--bg-weekend-color);
|
||||
}
|
||||
|
||||
&.sunday {
|
||||
background-color: var(--bg-weekend-color);
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
.bar-row {
|
||||
position: relative;
|
||||
|
||||
height: calc(var(--row-height) + 2 * var(--bar-margin));
|
||||
border-color: var(--separator-color);
|
||||
background-color: var(--bg-closed-color);
|
||||
|
||||
border-bottom: solid thin var(--separator-color);
|
||||
|
||||
.hour-separator {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
border-left: solid thin var(--separator-color);
|
||||
}
|
||||
|
||||
.interval {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
height: calc(var(--row-height) + 2 * var(--bar-margin));
|
||||
background-color: #fff;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.resize-action {
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background-color: red;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { IntervalPlanningComponent } from './interval-planning.component';
|
||||
|
||||
describe('IntervalPlanningComponent', () => {
|
||||
let component: IntervalPlanningComponent;
|
||||
let fixture: ComponentFixture<IntervalPlanningComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [IntervalPlanningComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(IntervalPlanningComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,83 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, HostListener, ViewChild } from '@angular/core';
|
||||
import { GanttView } from '../views/view';
|
||||
import { Observable } from 'rxjs';
|
||||
import { WeekdayNumbers } from 'luxon';
|
||||
import { _VisuallyHiddenLoader } from "@angular/cdk/private";
|
||||
|
||||
interface IntervalSet {
|
||||
[key: number]: IntervalProperties[],
|
||||
}
|
||||
|
||||
interface IntervalProperties {
|
||||
start: number,
|
||||
duration: number,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-interval-planning',
|
||||
imports: [],
|
||||
templateUrl: './interval-planning.component.html',
|
||||
styleUrl: './interval-planning.component.scss'
|
||||
})
|
||||
export class IntervalPlanningComponent {
|
||||
@ViewChild('barContainer') _barContainer!: ElementRef;
|
||||
|
||||
@HostListener('window:mousemove', ['$event'])
|
||||
onMouseMove(event: MouseEvent) {
|
||||
this.mouse = { x: event.clientX, y: event.clientY };
|
||||
|
||||
if (this.resizing) this.resize();
|
||||
}
|
||||
@HostListener('window:mouseup', ['$event'])
|
||||
onMouseUp(event: MouseEvent) {
|
||||
this.resizing = null;
|
||||
}
|
||||
|
||||
mouse = { x: 0, y: 0 };
|
||||
resizing: IntervalProperties | null = null;
|
||||
|
||||
view: GanttView;
|
||||
|
||||
globalBaseFontSize: number = 16;
|
||||
barContainerWidth: number = 0;
|
||||
|
||||
weekdays: WeekdayNumbers[] = [1, 2, 3, 4, 5, 6, 7];
|
||||
intervalSet: IntervalSet = { 2: [{ start: 3600, duration: 3600 }] }
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {
|
||||
this.view = new GanttView();
|
||||
this.view.changeView('day')
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
// get base font size
|
||||
let fontSize = window.getComputedStyle(this._barContainer.nativeElement).getPropertyValue('font-size');
|
||||
this.globalBaseFontSize = parseInt(fontSize.slice(0, fontSize.length - 2));
|
||||
this.view.globalBaseFontSize = parseInt(fontSize.slice(0, fontSize.length - 2));
|
||||
|
||||
// observe resizing of the bar container
|
||||
new Observable((observer) => {
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
observer.next();
|
||||
});
|
||||
resizeObserver.observe(this._barContainer.nativeElement);
|
||||
}).subscribe((change) => {
|
||||
console.log(this._barContainer.nativeElement.offsetWidth)
|
||||
this.barContainerWidth = this._barContainer.nativeElement.offsetWidth;
|
||||
this.view.width = this._barContainer.nativeElement.offsetWidth;
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
formatDayName(day: WeekdayNumbers): string {
|
||||
return this.view.viewType.start.set({ weekday: day }).toFormat('EEEE')
|
||||
}
|
||||
|
||||
resize(): void {
|
||||
// if (this.resizeCondMeet()) {
|
||||
if (this.resizing)
|
||||
this.resizing.duration += this.mouse.x;
|
||||
// }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user