From 24f1aedd1dbc9facd952441ef231e705d87e37ea Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Mon, 25 Nov 2024 17:37:28 +0800 Subject: [PATCH 01/14] feat(gantt): support quick time focus --- .../gantt-virtual-scroll/gantt.component.html | 1 + example/src/app/gantt/gantt.component.html | 1 + packages/gantt/src/components/icon/icons.ts | 6 ++- .../components/main/gantt-main.component.html | 26 ++++++++++++ .../components/main/gantt-main.component.ts | 18 +++++++-- packages/gantt/src/gantt-upper.ts | 29 +++++++++++++- packages/gantt/src/gantt.component.html | 2 + packages/gantt/src/gantt.component.scss | 40 +++++++++++++++++++ packages/gantt/src/gantt.component.ts | 7 +++- 9 files changed, 123 insertions(+), 7 deletions(-) diff --git a/example/src/app/gantt-virtual-scroll/gantt.component.html b/example/src/app/gantt-virtual-scroll/gantt.component.html index 97d28420..73f1b3c5 100644 --- a/example/src/app/gantt-virtual-scroll/gantt.component.html +++ b/example/src/app/gantt-virtual-scroll/gantt.component.html @@ -12,6 +12,7 @@ [virtualScrollEnabled]="true" (virtualScrolledIndexChange)="virtualScrolledIndexChange($event)" [loading]="loading" + [quickTimeFocus]="true" > `; const dragIcon = ``; +const arrowLeftIcon = ``; +const arrowRightIcon = ``; export const icons = { 'angle-right': angleRight, 'angle-down': angleDown, @@ -85,5 +87,7 @@ export const icons = { 'minus-square': minusSquare, loading: loadingIcon, empty: emptyIcon, - drag: dragIcon + drag: dragIcon, + 'arrow-left': arrowLeftIcon, + 'arrow-right': arrowRightIcon }; diff --git a/packages/gantt/src/components/main/gantt-main.component.html b/packages/gantt/src/components/main/gantt-main.component.html index a9fc0eb5..493e737b 100644 --- a/packages/gantt/src/components/main/gantt-main.component.html +++ b/packages/gantt/src/components/main/gantt-main.component.html @@ -35,3 +35,29 @@ + +@if (quickTimeFocus) { +
+
+ +
+ + @if (data.refs.x + data.refs.width < ganttUpper.scrollXRange().min && data.refs.width ) { + + + + } + + + + @if(data.refs.x > ganttUpper.scrollXRange().max && data.refs.width) { + + + + } + +
+
+
+
+} diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index 6ec1587b..240402f4 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter } from '@angular/core'; +import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef } from '@angular/core'; import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent, GanttLineClickEvent } from '../../class'; import { GANTT_UPPER_TOKEN, GanttUpper } from '../../gantt-upper'; import { IsGanttRangeItemPipe, IsGanttBarItemPipe, IsGanttCustomItemPipe } from '../../gantt.pipe'; @@ -7,6 +7,8 @@ import { NgxGanttBarComponent } from '../bar/bar.component'; import { NgxGanttRangeComponent } from '../range/range.component'; import { NgFor, NgIf, NgClass, NgTemplateOutlet } from '@angular/common'; import { GanttLinksComponent } from '../links/links.component'; +import { NgxGanttRootComponent } from 'ngx-gantt'; +import { GanttIconComponent } from '../icon/icon.component'; @Component({ selector: 'gantt-main', @@ -23,7 +25,8 @@ import { GanttLinksComponent } from '../links/links.component'; NgxGanttBaselineComponent, IsGanttRangeItemPipe, IsGanttBarItemPipe, - IsGanttCustomItemPipe + IsGanttCustomItemPipe, + GanttIconComponent ] }) export class GanttMainComponent { @@ -41,15 +44,24 @@ export class GanttMainComponent { @Input() baselineTemplate: TemplateRef; + @Input() ganttRoot: NgxGanttRootComponent; + + @Input() quickTimeFocus: boolean; + @Output() barClick = new EventEmitter(); @Output() lineClick = new EventEmitter(); @HostBinding('class.gantt-main-container') ganttMainClass = true; - constructor(@Inject(GANTT_UPPER_TOKEN) public ganttUpper: GanttUpper) {} + constructor(@Inject(GANTT_UPPER_TOKEN) public ganttUpper: GanttUpper, public elementRef: ElementRef) {} trackBy(index: number, item: GanttGroupInternal | GanttItemInternal) { return item.id || index; } + + quickTime(item: GanttItemInternal) { + const date = item.start ? item.start : item.end; + this.ganttRoot.scrollToDate(date); + } } diff --git a/packages/gantt/src/gantt-upper.ts b/packages/gantt/src/gantt-upper.ts index 19d0db47..45646dc8 100644 --- a/packages/gantt/src/gantt-upper.ts +++ b/packages/gantt/src/gantt-upper.ts @@ -14,7 +14,9 @@ import { Inject, OnInit, OnDestroy, - OnChanges + OnChanges, + WritableSignal, + signal } from '@angular/core'; import { from, Subject } from 'rxjs'; import { takeUntil, take, skip } from 'rxjs/operators'; @@ -177,6 +179,12 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { private _linkOptions: GanttLinkOptions; + public scrollXRange: WritableSignal<{ min: number; max: number }> = signal({ min: 0, max: 0 }); + + private ganttMainElement: HTMLElement | null = null; + + private resizeObserver: ResizeObserver | null = null; + @HostBinding('class.gantt') ganttClass = true; constructor( @@ -344,6 +352,12 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { this.disabledLoadOnScroll = disabledLoadOnScroll; this.dragEnded.emit(event); }); + + this.ganttMainElement = document.querySelector('gantt-main'); + this.updateXCoordinate(); + this.ganttMainElement.addEventListener('scroll', this.updateXCoordinate); + this.resizeObserver = new ResizeObserver(() => this.updateXCoordinate()); + this.resizeObserver.observe(this.ganttMainElement); }); }); @@ -377,6 +391,12 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); + if (this.ganttMainElement) { + this.ganttMainElement.removeEventListener('scroll', this.updateXCoordinate); + } + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + } } computeItemsRefs(...items: GanttItemInternal[] | GanttBaselineItemInternal[]) { @@ -451,6 +471,13 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { rerenderView() { this.changeView(this.viewType); } + + private updateXCoordinate = () => { + const { scrollLeft, clientWidth } = this.ganttMainElement!; + const min = scrollLeft; + const max = scrollLeft + clientWidth; + this.scrollXRange.set({ min, max }); + }; } export const GANTT_UPPER_TOKEN = new InjectionToken('GANTT_UPPER_TOKEN'); diff --git a/packages/gantt/src/gantt.component.html b/packages/gantt/src/gantt.component.html index ea3d7866..6113ac10 100644 --- a/packages/gantt/src/gantt.component.html +++ b/packages/gantt/src/gantt.component.html @@ -50,6 +50,7 @@ >
diff --git a/packages/gantt/src/gantt.component.scss b/packages/gantt/src/gantt.component.scss index 363e4218..321bf391 100644 --- a/packages/gantt/src/gantt.component.scss +++ b/packages/gantt/src/gantt.component.scss @@ -110,6 +110,46 @@ background-color: rgba($color: variables.$gantt-table-header-drag-line-color, $alpha: 0.1); } } + + .gantt-quick-time-focus-container { + position: absolute; + left: 0; + top: 0; + .gantt-quick-time-focus { + position: sticky; + left: 0; + width: 0px; + z-index: 3; + pointer-events: none; + + &-item { + display: flex; + justify-content: space-between; + align-items: center; + + &-arrow { + width: 20px; + height: 20px; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + background-color: variables.$gantt-bar-bg; + border: 1px solid variables.$gantt-border-color; + border-radius: 4px; + box-shadow: 0 4px 7px 1px rgba(0, 0, 0, 0.05); + pointer-events: all; + .gantt-icon { + display: inline-block; + svg { + width: 14px; + height: 14px; + } + } + } + } + } + } } .gantt-normal-viewport { diff --git a/packages/gantt/src/gantt.component.ts b/packages/gantt/src/gantt.component.ts index 26377c10..dc0ff494 100644 --- a/packages/gantt/src/gantt.component.ts +++ b/packages/gantt/src/gantt.component.ts @@ -1,5 +1,5 @@ import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport, ViewportRuler } from '@angular/cdk/scrolling'; -import { NgClass, NgIf, NgTemplateOutlet } from '@angular/common'; +import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'; import { AfterViewChecked, AfterViewInit, @@ -82,7 +82,8 @@ import { GanttScrollbarComponent } from './components/scrollbar/scrollbar.compon GanttMainComponent, GanttDragBackdropComponent, GanttScrollbarComponent, - NgTemplateOutlet + NgTemplateOutlet, + NgFor ] }) export class NgxGanttComponent extends GanttUpper implements OnInit, OnChanges, AfterViewInit, AfterViewChecked { @@ -114,6 +115,8 @@ export class NgxGanttComponent extends GanttUpper implements OnInit, OnChanges, @Input() loadingDelay = 0; + @Input() quickTimeFocus = false; + @Output() linkDragStarted = new EventEmitter(); @Output() override linkDragEnded = new EventEmitter(); From e02e6f28c715cc534433e04e0457ef8fa139372f Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 09:44:52 +0800 Subject: [PATCH 02/14] feat(gantt): support quick time focus --- .../components/main/gantt-main.component.html | 4 +- .../components/main/gantt-main.component.ts | 39 +++++++++++++++++-- packages/gantt/src/gantt-dom.service.ts | 23 ++++++++++- packages/gantt/src/gantt-upper.ts | 25 ------------ packages/gantt/src/root.component.ts | 2 +- 5 files changed, 60 insertions(+), 33 deletions(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.html b/packages/gantt/src/components/main/gantt-main.component.html index 493e737b..de8b0fd7 100644 --- a/packages/gantt/src/components/main/gantt-main.component.html +++ b/packages/gantt/src/components/main/gantt-main.component.html @@ -42,7 +42,7 @@
- @if (data.refs.x + data.refs.width < ganttUpper.scrollXRange().min && data.refs.width ) { + @if (data.refs.x + data.refs.width < dom.visibleRangeX().min && data.refs.width ) { @@ -50,7 +50,7 @@ - @if(data.refs.x > ganttUpper.scrollXRange().max && data.refs.width) { + @if(data.refs.x > dom.visibleRangeX().max && data.refs.width) { diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index 240402f4..e4747a46 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef } from '@angular/core'; +import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef, OnInit, NgZone } from '@angular/core'; import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent, GanttLineClickEvent } from '../../class'; import { GANTT_UPPER_TOKEN, GanttUpper } from '../../gantt-upper'; import { IsGanttRangeItemPipe, IsGanttBarItemPipe, IsGanttCustomItemPipe } from '../../gantt.pipe'; @@ -9,6 +9,8 @@ import { NgFor, NgIf, NgClass, NgTemplateOutlet } from '@angular/common'; import { GanttLinksComponent } from '../links/links.component'; import { NgxGanttRootComponent } from 'ngx-gantt'; import { GanttIconComponent } from '../icon/icon.component'; +import { GanttDomService } from 'ngx-gantt/gantt-dom.service'; +import { from, Subject, take, takeUntil } from 'rxjs'; @Component({ selector: 'gantt-main', @@ -29,7 +31,7 @@ import { GanttIconComponent } from '../icon/icon.component'; GanttIconComponent ] }) -export class GanttMainComponent { +export class GanttMainComponent implements OnInit { @Input() viewportItems: (GanttGroupInternal | GanttItemInternal)[]; @Input() flatItems: (GanttGroupInternal | GanttItemInternal)[]; @@ -54,12 +56,43 @@ export class GanttMainComponent { @HostBinding('class.gantt-main-container') ganttMainClass = true; - constructor(@Inject(GANTT_UPPER_TOKEN) public ganttUpper: GanttUpper, public elementRef: ElementRef) {} + private unsubscribe$ = new Subject(); + + constructor( + @Inject(GANTT_UPPER_TOKEN) public ganttUpper: GanttUpper, + public elementRef: ElementRef, + public dom: GanttDomService, + protected ngZone: NgZone + ) {} + + ngOnInit(): void { + const onStable$ = this.ngZone.isStable ? from(Promise.resolve()) : this.ngZone.onStable.pipe(take(1)); + + this.ngZone.runOutsideAngular(() => { + onStable$.pipe(takeUntil(this.unsubscribe$)).subscribe(() => { + this.setupResize(); + this.dom.checkAndSetViewScroll(this.setupViewScroll); + }); + }); + } + + setupViewScroll = () => { + return this.dom.getViewerScroll().pipe(takeUntil(this.unsubscribe$)).subscribe(); + }; trackBy(index: number, item: GanttGroupInternal | GanttItemInternal) { return item.id || index; } + private setupResize() { + this.dom + .getResize() + .pipe(takeUntil(this.unsubscribe$)) + .subscribe(() => { + this.dom.setVisibleRangeX(); + }); + } + quickTime(item: GanttItemInternal) { const date = item.start ? item.start : item.end; this.ganttRoot.scrollToDate(date); diff --git a/packages/gantt/src/gantt-dom.service.ts b/packages/gantt/src/gantt-dom.service.ts index 6e1b26bc..daa7b630 100644 --- a/packages/gantt/src/gantt-dom.service.ts +++ b/packages/gantt/src/gantt-dom.service.ts @@ -1,6 +1,6 @@ import { isPlatformServer } from '@angular/common'; -import { Injectable, ElementRef, OnDestroy, Inject, PLATFORM_ID, NgZone } from '@angular/core'; -import { fromEvent, Subject, merge, EMPTY, Observable } from 'rxjs'; +import { Injectable, ElementRef, OnDestroy, Inject, PLATFORM_ID, NgZone, WritableSignal, signal } from '@angular/core'; +import { fromEvent, Subject, merge, EMPTY, Observable, Subscription } from 'rxjs'; import { pairwise, map, auditTime, takeUntil } from 'rxjs/operators'; import { isNumber } from './utils/helpers'; import { passiveListenerOptions } from './utils/passive-listeners'; @@ -40,6 +40,10 @@ export class GanttDomService implements OnDestroy { public linksOverlay: Element; + public isViewScrollSet = false; + + public visibleRangeX: WritableSignal<{ min: number; max: number }> = signal({ min: 0, max: 0 }); + private mainFooter: Element; private mainScrollbar: Element; @@ -48,6 +52,13 @@ export class GanttDomService implements OnDestroy { constructor(private ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: string) {} + checkAndSetViewScroll = (viewerScroll: () => Subscription) => { + if (!this.isViewScrollSet) { + this.isViewScrollSet = true; + return viewerScroll(); + } + }; + private monitorScrollChange() { const scrollObservers = [ fromEvent(this.mainContainer, 'scroll', passiveListenerOptions), @@ -141,6 +152,7 @@ export class GanttDomService implements OnDestroy { map(() => this.mainContainer.scrollLeft), pairwise(), map(([previous, current]) => { + this.setVisibleRangeX(); const event: ScrollEvent = { target: this.mainContainer, direction: ScrollDirection.NONE @@ -181,6 +193,13 @@ export class GanttDomService implements OnDestroy { } } + setVisibleRangeX() { + this.visibleRangeX.set({ + min: this.mainContainer.scrollLeft, + max: this.mainContainer.scrollLeft + this.mainContainer.clientWidth + }); + } + ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); diff --git a/packages/gantt/src/gantt-upper.ts b/packages/gantt/src/gantt-upper.ts index 45646dc8..f2e0bd82 100644 --- a/packages/gantt/src/gantt-upper.ts +++ b/packages/gantt/src/gantt-upper.ts @@ -179,12 +179,6 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { private _linkOptions: GanttLinkOptions; - public scrollXRange: WritableSignal<{ min: number; max: number }> = signal({ min: 0, max: 0 }); - - private ganttMainElement: HTMLElement | null = null; - - private resizeObserver: ResizeObserver | null = null; - @HostBinding('class.gantt') ganttClass = true; constructor( @@ -352,12 +346,6 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { this.disabledLoadOnScroll = disabledLoadOnScroll; this.dragEnded.emit(event); }); - - this.ganttMainElement = document.querySelector('gantt-main'); - this.updateXCoordinate(); - this.ganttMainElement.addEventListener('scroll', this.updateXCoordinate); - this.resizeObserver = new ResizeObserver(() => this.updateXCoordinate()); - this.resizeObserver.observe(this.ganttMainElement); }); }); @@ -391,12 +379,6 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); - if (this.ganttMainElement) { - this.ganttMainElement.removeEventListener('scroll', this.updateXCoordinate); - } - if (this.resizeObserver) { - this.resizeObserver.disconnect(); - } } computeItemsRefs(...items: GanttItemInternal[] | GanttBaselineItemInternal[]) { @@ -471,13 +453,6 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { rerenderView() { this.changeView(this.viewType); } - - private updateXCoordinate = () => { - const { scrollLeft, clientWidth } = this.ganttMainElement!; - const min = scrollLeft; - const max = scrollLeft + clientWidth; - this.scrollXRange.set({ min, max }); - }; } export const GANTT_UPPER_TOKEN = new InjectionToken('GANTT_UPPER_TOKEN'); diff --git a/packages/gantt/src/root.component.ts b/packages/gantt/src/root.component.ts index a492c728..6c234a76 100644 --- a/packages/gantt/src/root.component.ts +++ b/packages/gantt/src/root.component.ts @@ -127,7 +127,7 @@ export class NgxGanttRootComponent implements OnInit, OnDestroy { } private setupViewScroll() { - if (this.ganttUpper.disabledLoadOnScroll) { + if (this.ganttUpper.disabledLoadOnScroll || this.dom.isViewScrollSet) { return; } this.dom From 57be5b4184e552bbf4baa0843f7219edb60f1f33 Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 09:47:16 +0800 Subject: [PATCH 03/14] feat(gantt): support quick time focus --- packages/gantt/src/gantt-upper.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/gantt/src/gantt-upper.ts b/packages/gantt/src/gantt-upper.ts index f2e0bd82..19d0db47 100644 --- a/packages/gantt/src/gantt-upper.ts +++ b/packages/gantt/src/gantt-upper.ts @@ -14,9 +14,7 @@ import { Inject, OnInit, OnDestroy, - OnChanges, - WritableSignal, - signal + OnChanges } from '@angular/core'; import { from, Subject } from 'rxjs'; import { takeUntil, take, skip } from 'rxjs/operators'; From 81a3a7e23e397d744fa0bfbd610db26db3556919 Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 09:56:59 +0800 Subject: [PATCH 04/14] feat(gantt): support quick time focus --- packages/gantt/src/components/main/gantt-main.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index e4747a46..ae3a98e8 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -9,7 +9,7 @@ import { NgFor, NgIf, NgClass, NgTemplateOutlet } from '@angular/common'; import { GanttLinksComponent } from '../links/links.component'; import { NgxGanttRootComponent } from 'ngx-gantt'; import { GanttIconComponent } from '../icon/icon.component'; -import { GanttDomService } from 'ngx-gantt/gantt-dom.service'; +import { GanttDomService } from '../../gantt-dom.service'; import { from, Subject, take, takeUntil } from 'rxjs'; @Component({ From 29552ffe8e33d69a9701300b12e24e05bafb2db5 Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 10:01:33 +0800 Subject: [PATCH 05/14] feat(gantt): support quick time focus --- packages/gantt/src/components/main/gantt-main.component.ts | 4 ++-- packages/gantt/src/gantt-dom.service.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index ae3a98e8..56f000c0 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -71,13 +71,13 @@ export class GanttMainComponent implements OnInit { this.ngZone.runOutsideAngular(() => { onStable$.pipe(takeUntil(this.unsubscribe$)).subscribe(() => { this.setupResize(); - this.dom.checkAndSetViewScroll(this.setupViewScroll); + this.dom.checkAndSetViewScroll(this.setupViewScroll).subscribe(); }); }); } setupViewScroll = () => { - return this.dom.getViewerScroll().pipe(takeUntil(this.unsubscribe$)).subscribe(); + return this.dom.getViewerScroll().pipe(takeUntil(this.unsubscribe$)); }; trackBy(index: number, item: GanttGroupInternal | GanttItemInternal) { diff --git a/packages/gantt/src/gantt-dom.service.ts b/packages/gantt/src/gantt-dom.service.ts index daa7b630..e1e6cf0d 100644 --- a/packages/gantt/src/gantt-dom.service.ts +++ b/packages/gantt/src/gantt-dom.service.ts @@ -52,7 +52,7 @@ export class GanttDomService implements OnDestroy { constructor(private ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: string) {} - checkAndSetViewScroll = (viewerScroll: () => Subscription) => { + checkAndSetViewScroll = (viewerScroll: () => Observable) => { if (!this.isViewScrollSet) { this.isViewScrollSet = true; return viewerScroll(); From e56f2f6b14b300889d67d4e902520702dcafb098 Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 12:15:07 +0800 Subject: [PATCH 06/14] feat(gantt): support quick time focus --- .../components/main/gantt-main.component.html | 12 ++++---- .../components/main/gantt-main.component.ts | 30 ++++++++++--------- packages/gantt/src/gantt-upper.ts | 2 ++ packages/gantt/src/gantt.component.scss | 15 +++++++++- packages/gantt/src/gantt.component.ts | 2 -- packages/gantt/src/root.component.ts | 2 +- 6 files changed, 39 insertions(+), 24 deletions(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.html b/packages/gantt/src/components/main/gantt-main.component.html index de8b0fd7..657de45f 100644 --- a/packages/gantt/src/components/main/gantt-main.component.html +++ b/packages/gantt/src/components/main/gantt-main.component.html @@ -41,17 +41,17 @@
- - @if (data.refs.x + data.refs.width < dom.visibleRangeX().min && data.refs.width ) { - + + @if ((data.refs.x + data.refs.width < dom.visibleRangeX().min || data.refs.x < dom.visibleRangeX().min) && data.refs.width ) { + } - - @if(data.refs.x > dom.visibleRangeX().max && data.refs.width) { - + + @if((data.refs.x > dom.visibleRangeX().max || data.refs.x + data.refs.width > dom.visibleRangeX().max) && data.refs.width) { + } diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index 56f000c0..fd058a02 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef, OnInit, NgZone } from '@angular/core'; +import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef, NgZone, AfterViewInit } from '@angular/core'; import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent, GanttLineClickEvent } from '../../class'; import { GANTT_UPPER_TOKEN, GanttUpper } from '../../gantt-upper'; import { IsGanttRangeItemPipe, IsGanttBarItemPipe, IsGanttCustomItemPipe } from '../../gantt.pipe'; @@ -10,7 +10,7 @@ import { GanttLinksComponent } from '../links/links.component'; import { NgxGanttRootComponent } from 'ngx-gantt'; import { GanttIconComponent } from '../icon/icon.component'; import { GanttDomService } from '../../gantt-dom.service'; -import { from, Subject, take, takeUntil } from 'rxjs'; +import { Subject, takeUntil } from 'rxjs'; @Component({ selector: 'gantt-main', @@ -31,7 +31,7 @@ import { from, Subject, take, takeUntil } from 'rxjs'; GanttIconComponent ] }) -export class GanttMainComponent implements OnInit { +export class GanttMainComponent implements AfterViewInit { @Input() viewportItems: (GanttGroupInternal | GanttItemInternal)[]; @Input() flatItems: (GanttGroupInternal | GanttItemInternal)[]; @@ -65,15 +65,11 @@ export class GanttMainComponent implements OnInit { protected ngZone: NgZone ) {} - ngOnInit(): void { - const onStable$ = this.ngZone.isStable ? from(Promise.resolve()) : this.ngZone.onStable.pipe(take(1)); - - this.ngZone.runOutsideAngular(() => { - onStable$.pipe(takeUntil(this.unsubscribe$)).subscribe(() => { - this.setupResize(); - this.dom.checkAndSetViewScroll(this.setupViewScroll).subscribe(); - }); - }); + ngAfterViewInit(): void { + this.setupResize(); + if (this.dom.mainContainer) { + this.dom.checkAndSetViewScroll(this.setupViewScroll).subscribe(); + } } setupViewScroll = () => { @@ -93,8 +89,14 @@ export class GanttMainComponent implements OnInit { }); } - quickTime(item: GanttItemInternal) { - const date = item.start ? item.start : item.end; + quickTime(item: GanttItemInternal, type: 'left' | 'right') { + let date; + if (type === 'left') { + date = item.start ? item.start : item.end; + } + if (type === 'right') { + date = item.end ? item.end : item.start; + } this.ganttRoot.scrollToDate(date); } } diff --git a/packages/gantt/src/gantt-upper.ts b/packages/gantt/src/gantt-upper.ts index 19d0db47..678064e5 100644 --- a/packages/gantt/src/gantt-upper.ts +++ b/packages/gantt/src/gantt-upper.ts @@ -109,6 +109,8 @@ export abstract class GanttUpper implements OnChanges, OnInit, OnDestroy { return this._multiple; } + @Input() quickTimeFocus = false; + @Output() loadOnScroll = new EventEmitter(); @Output() dragStarted = new EventEmitter(); diff --git a/packages/gantt/src/gantt.component.scss b/packages/gantt/src/gantt.component.scss index 321bf391..7ee4eec9 100644 --- a/packages/gantt/src/gantt.component.scss +++ b/packages/gantt/src/gantt.component.scss @@ -126,6 +126,20 @@ display: flex; justify-content: space-between; align-items: center; + span { + width: 22px; + height: 22px; + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 2px; + pointer-events: all; + &:hover { + .gantt-quick-time-focus-item-arrow { + border: 1px solid rgba(variables.$gantt-primary-color, 1); + } + } + } &-arrow { width: 20px; @@ -138,7 +152,6 @@ border: 1px solid variables.$gantt-border-color; border-radius: 4px; box-shadow: 0 4px 7px 1px rgba(0, 0, 0, 0.05); - pointer-events: all; .gantt-icon { display: inline-block; svg { diff --git a/packages/gantt/src/gantt.component.ts b/packages/gantt/src/gantt.component.ts index dc0ff494..3521dc62 100644 --- a/packages/gantt/src/gantt.component.ts +++ b/packages/gantt/src/gantt.component.ts @@ -115,8 +115,6 @@ export class NgxGanttComponent extends GanttUpper implements OnInit, OnChanges, @Input() loadingDelay = 0; - @Input() quickTimeFocus = false; - @Output() linkDragStarted = new EventEmitter(); @Output() override linkDragEnded = new EventEmitter(); diff --git a/packages/gantt/src/root.component.ts b/packages/gantt/src/root.component.ts index 6c234a76..487876b1 100644 --- a/packages/gantt/src/root.component.ts +++ b/packages/gantt/src/root.component.ts @@ -127,7 +127,7 @@ export class NgxGanttRootComponent implements OnInit, OnDestroy { } private setupViewScroll() { - if (this.ganttUpper.disabledLoadOnScroll || this.dom.isViewScrollSet) { + if (this.ganttUpper.disabledLoadOnScroll && !this.ganttUpper.quickTimeFocus) { return; } this.dom From f027c74e7beacd920b72cb425eedf64e27c6422e Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 12:25:17 +0800 Subject: [PATCH 07/14] feat(gantt): support quick time focus --- packages/gantt/src/components/main/gantt-main.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.html b/packages/gantt/src/components/main/gantt-main.component.html index 657de45f..25a5ee54 100644 --- a/packages/gantt/src/components/main/gantt-main.component.html +++ b/packages/gantt/src/components/main/gantt-main.component.html @@ -42,7 +42,7 @@
- @if ((data.refs.x + data.refs.width < dom.visibleRangeX().min || data.refs.x < dom.visibleRangeX().min) && data.refs.width ) { + @if ((data.refs.x < dom.visibleRangeX().min ) && data.refs.width ) { @@ -50,7 +50,7 @@ - @if((data.refs.x > dom.visibleRangeX().max || data.refs.x + data.refs.width > dom.visibleRangeX().max) && data.refs.width) { + @if((data.refs.x + data.refs.width > dom.visibleRangeX().max) && data.refs.width) { From cc7d3b2d0ea1a84b6f00740f720d681d5a38f872 Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 12:26:03 +0800 Subject: [PATCH 08/14] feat(gantt): support quick time focus --- packages/gantt/src/components/main/gantt-main.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.html b/packages/gantt/src/components/main/gantt-main.component.html index 25a5ee54..0737d167 100644 --- a/packages/gantt/src/components/main/gantt-main.component.html +++ b/packages/gantt/src/components/main/gantt-main.component.html @@ -49,7 +49,7 @@ } - + @if((data.refs.x + data.refs.width > dom.visibleRangeX().max) && data.refs.width) { From 484dd744b98a7b0ea8e4133e744c514861020cef Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 13:42:40 +0800 Subject: [PATCH 09/14] feat(gantt): support quick time focus --- .../gantt/src/components/main/gantt-main.component.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index fd058a02..68c3ab5c 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef, NgZone, AfterViewInit } from '@angular/core'; +import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef, NgZone, OnInit } from '@angular/core'; import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent, GanttLineClickEvent } from '../../class'; import { GANTT_UPPER_TOKEN, GanttUpper } from '../../gantt-upper'; import { IsGanttRangeItemPipe, IsGanttBarItemPipe, IsGanttCustomItemPipe } from '../../gantt.pipe'; @@ -31,7 +31,7 @@ import { Subject, takeUntil } from 'rxjs'; GanttIconComponent ] }) -export class GanttMainComponent implements AfterViewInit { +export class GanttMainComponent implements OnInit { @Input() viewportItems: (GanttGroupInternal | GanttItemInternal)[]; @Input() flatItems: (GanttGroupInternal | GanttItemInternal)[]; @@ -65,11 +65,8 @@ export class GanttMainComponent implements AfterViewInit { protected ngZone: NgZone ) {} - ngAfterViewInit(): void { + ngOnInit(): void { this.setupResize(); - if (this.dom.mainContainer) { - this.dom.checkAndSetViewScroll(this.setupViewScroll).subscribe(); - } } setupViewScroll = () => { From 7fd924459c56deab2429e70ec0b1c071649fd37a Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 13:52:35 +0800 Subject: [PATCH 10/14] feat(gantt): support quick time focus --- .../gantt/src/components/main/gantt-main.component.ts | 10 +++------- packages/gantt/src/gantt-dom.service.ts | 9 --------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index 68c3ab5c..fcbbeafe 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef, NgZone, OnInit } from '@angular/core'; +import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef, NgZone, AfterViewInit } from '@angular/core'; import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent, GanttLineClickEvent } from '../../class'; import { GANTT_UPPER_TOKEN, GanttUpper } from '../../gantt-upper'; import { IsGanttRangeItemPipe, IsGanttBarItemPipe, IsGanttCustomItemPipe } from '../../gantt.pipe'; @@ -31,7 +31,7 @@ import { Subject, takeUntil } from 'rxjs'; GanttIconComponent ] }) -export class GanttMainComponent implements OnInit { +export class GanttMainComponent implements AfterViewInit { @Input() viewportItems: (GanttGroupInternal | GanttItemInternal)[]; @Input() flatItems: (GanttGroupInternal | GanttItemInternal)[]; @@ -65,14 +65,10 @@ export class GanttMainComponent implements OnInit { protected ngZone: NgZone ) {} - ngOnInit(): void { + ngAfterViewInit(): void { this.setupResize(); } - setupViewScroll = () => { - return this.dom.getViewerScroll().pipe(takeUntil(this.unsubscribe$)); - }; - trackBy(index: number, item: GanttGroupInternal | GanttItemInternal) { return item.id || index; } diff --git a/packages/gantt/src/gantt-dom.service.ts b/packages/gantt/src/gantt-dom.service.ts index e1e6cf0d..3a38e236 100644 --- a/packages/gantt/src/gantt-dom.service.ts +++ b/packages/gantt/src/gantt-dom.service.ts @@ -40,8 +40,6 @@ export class GanttDomService implements OnDestroy { public linksOverlay: Element; - public isViewScrollSet = false; - public visibleRangeX: WritableSignal<{ min: number; max: number }> = signal({ min: 0, max: 0 }); private mainFooter: Element; @@ -52,13 +50,6 @@ export class GanttDomService implements OnDestroy { constructor(private ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: string) {} - checkAndSetViewScroll = (viewerScroll: () => Observable) => { - if (!this.isViewScrollSet) { - this.isViewScrollSet = true; - return viewerScroll(); - } - }; - private monitorScrollChange() { const scrollObservers = [ fromEvent(this.mainContainer, 'scroll', passiveListenerOptions), From 43f8745bc04e21a3ebdb9e05abb7da383e2a890e Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 13:56:04 +0800 Subject: [PATCH 11/14] feat(gantt): support quick time focus --- .../components/main/gantt-main.component.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index fcbbeafe..73d315c5 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -1,4 +1,16 @@ -import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ElementRef, NgZone, AfterViewInit } from '@angular/core'; +import { + Component, + HostBinding, + Inject, + Input, + TemplateRef, + Output, + EventEmitter, + ElementRef, + NgZone, + AfterViewInit, + OnInit +} from '@angular/core'; import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent, GanttLineClickEvent } from '../../class'; import { GANTT_UPPER_TOKEN, GanttUpper } from '../../gantt-upper'; import { IsGanttRangeItemPipe, IsGanttBarItemPipe, IsGanttCustomItemPipe } from '../../gantt.pipe'; @@ -31,7 +43,7 @@ import { Subject, takeUntil } from 'rxjs'; GanttIconComponent ] }) -export class GanttMainComponent implements AfterViewInit { +export class GanttMainComponent implements OnInit { @Input() viewportItems: (GanttGroupInternal | GanttItemInternal)[]; @Input() flatItems: (GanttGroupInternal | GanttItemInternal)[]; @@ -65,7 +77,7 @@ export class GanttMainComponent implements AfterViewInit { protected ngZone: NgZone ) {} - ngAfterViewInit(): void { + ngOnInit(): void { this.setupResize(); } @@ -83,13 +95,7 @@ export class GanttMainComponent implements AfterViewInit { } quickTime(item: GanttItemInternal, type: 'left' | 'right') { - let date; - if (type === 'left') { - date = item.start ? item.start : item.end; - } - if (type === 'right') { - date = item.end ? item.end : item.start; - } + const date = type === 'left' ? item.start || item.end : item.end || item.start; this.ganttRoot.scrollToDate(date); } } From 5893e9d9435168cc6df32c248f069b50310bf379 Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 13:57:01 +0800 Subject: [PATCH 12/14] feat(gantt): support quick time focus --- packages/gantt/src/gantt-dom.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gantt/src/gantt-dom.service.ts b/packages/gantt/src/gantt-dom.service.ts index 3a38e236..000c99e5 100644 --- a/packages/gantt/src/gantt-dom.service.ts +++ b/packages/gantt/src/gantt-dom.service.ts @@ -1,6 +1,6 @@ import { isPlatformServer } from '@angular/common'; import { Injectable, ElementRef, OnDestroy, Inject, PLATFORM_ID, NgZone, WritableSignal, signal } from '@angular/core'; -import { fromEvent, Subject, merge, EMPTY, Observable, Subscription } from 'rxjs'; +import { fromEvent, Subject, merge, EMPTY, Observable } from 'rxjs'; import { pairwise, map, auditTime, takeUntil } from 'rxjs/operators'; import { isNumber } from './utils/helpers'; import { passiveListenerOptions } from './utils/passive-listeners'; From 0afd81bf0e070db0db529aaba2c024d1b082c4e9 Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 14:00:11 +0800 Subject: [PATCH 13/14] feat(gantt): support quick time focus --- packages/gantt/src/gantt.component.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gantt/src/gantt.component.scss b/packages/gantt/src/gantt.component.scss index 7ee4eec9..f0b4a987 100644 --- a/packages/gantt/src/gantt.component.scss +++ b/packages/gantt/src/gantt.component.scss @@ -127,8 +127,8 @@ justify-content: space-between; align-items: center; span { - width: 22px; - height: 22px; + width: 24px; + height: 24px; display: flex; justify-content: center; align-items: center; From 09aa9786d50bcdf27eb38e423ef3eeb5543a873b Mon Sep 17 00:00:00 2001 From: smile1016 <1017652509@qq.com> Date: Tue, 26 Nov 2024 16:33:27 +0800 Subject: [PATCH 14/14] feat(gantt): fix bug of virtualScroll and add element resize --- .../components/main/gantt-main.component.html | 2 +- .../components/main/gantt-main.component.ts | 33 ++++++------------- packages/gantt/src/gantt-dom.service.ts | 9 +++++ 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/packages/gantt/src/components/main/gantt-main.component.html b/packages/gantt/src/components/main/gantt-main.component.html index 0737d167..2b1412aa 100644 --- a/packages/gantt/src/components/main/gantt-main.component.html +++ b/packages/gantt/src/components/main/gantt-main.component.html @@ -38,7 +38,7 @@ @if (quickTimeFocus) {
-
+
diff --git a/packages/gantt/src/components/main/gantt-main.component.ts b/packages/gantt/src/components/main/gantt-main.component.ts index 73d315c5..21dfee99 100644 --- a/packages/gantt/src/components/main/gantt-main.component.ts +++ b/packages/gantt/src/components/main/gantt-main.component.ts @@ -1,16 +1,4 @@ -import { - Component, - HostBinding, - Inject, - Input, - TemplateRef, - Output, - EventEmitter, - ElementRef, - NgZone, - AfterViewInit, - OnInit -} from '@angular/core'; +import { Component, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, OnInit, AfterViewInit, NgZone } from '@angular/core'; import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent, GanttLineClickEvent } from '../../class'; import { GANTT_UPPER_TOKEN, GanttUpper } from '../../gantt-upper'; import { IsGanttRangeItemPipe, IsGanttBarItemPipe, IsGanttCustomItemPipe } from '../../gantt.pipe'; @@ -22,7 +10,7 @@ import { GanttLinksComponent } from '../links/links.component'; import { NgxGanttRootComponent } from 'ngx-gantt'; import { GanttIconComponent } from '../icon/icon.component'; import { GanttDomService } from '../../gantt-dom.service'; -import { Subject, takeUntil } from 'rxjs'; +import { combineLatest, from, Subject, take, takeUntil } from 'rxjs'; @Component({ selector: 'gantt-main', @@ -70,15 +58,15 @@ export class GanttMainComponent implements OnInit { private unsubscribe$ = new Subject(); - constructor( - @Inject(GANTT_UPPER_TOKEN) public ganttUpper: GanttUpper, - public elementRef: ElementRef, - public dom: GanttDomService, - protected ngZone: NgZone - ) {} + constructor(@Inject(GANTT_UPPER_TOKEN) public ganttUpper: GanttUpper, public dom: GanttDomService, protected ngZone: NgZone) {} ngOnInit(): void { - this.setupResize(); + const onStable$ = this.ngZone.isStable ? from(Promise.resolve()) : this.ngZone.onStable.pipe(take(1)); + this.ngZone.runOutsideAngular(() => { + onStable$.pipe(takeUntil(this.unsubscribe$)).subscribe(() => { + this.setupResize(); + }); + }); } trackBy(index: number, item: GanttGroupInternal | GanttItemInternal) { @@ -86,8 +74,7 @@ export class GanttMainComponent implements OnInit { } private setupResize() { - this.dom - .getResize() + combineLatest([this.dom.getResize(), this.dom.getResizeByElement(this.dom.mainContainer)]) .pipe(takeUntil(this.unsubscribe$)) .subscribe(() => { this.dom.setVisibleRangeX(); diff --git a/packages/gantt/src/gantt-dom.service.ts b/packages/gantt/src/gantt-dom.service.ts index 000c99e5..e6a69b58 100644 --- a/packages/gantt/src/gantt-dom.service.ts +++ b/packages/gantt/src/gantt-dom.service.ts @@ -173,6 +173,15 @@ export class GanttDomService implements OnDestroy { return isPlatformServer(this.platformId) ? EMPTY : fromEvent(window, 'resize').pipe(auditTime(150)); } + getResizeByElement(element: Element) { + return new Observable((observer) => { + const resizeObserver = new ResizeObserver(() => { + observer.next(); + }); + resizeObserver.observe(element); + }); + } + scrollMainContainer(left: number) { if (isNumber(left)) { const scrollLeft = left - this.mainContainer.clientWidth / 2;