Skip to content

Commit

Permalink
feat(module:core): support reset NZ_CONFIG inside component & overflo…
Browse files Browse the repository at this point in the history
…w component (#6601)

* feat(module:config): support reset NZ_CONFIG inside component

* refactor: add onDestroy service

* feat: add cdk entry

* refactor: rebase eslint

* feat: support overflow
  • Loading branch information
vthinkxie authored Jun 16, 2021
1 parent 8ad6a18 commit edd410a
Show file tree
Hide file tree
Showing 33 changed files with 720 additions and 107 deletions.
File renamed without changes.
163 changes: 163 additions & 0 deletions components/cdk/overflow/overflow-container.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import {
Component,
ChangeDetectionStrategy,
ContentChildren,
QueryList,
ElementRef,
OnInit,
AfterContentInit,
OnDestroy,
ContentChild,
ChangeDetectorRef
} from '@angular/core';
import { BehaviorSubject, combineLatest, Observable, ReplaySubject, Subject } from 'rxjs';
import { filter, map, pairwise, startWith, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';

import { NzResizeObserver } from 'ng-zorro-antd/cdk/resize-observer';

import { NzOverflowItemDirective } from './overflow-item.directive';
import { NzOverflowRestDirective } from './overflow-rest.directive';
import { NzOverflowSuffixDirective } from './overflow-suffix.directive';

@Component({
selector: 'nz-overflow-container',
template: ` <ng-content></ng-content>
<ng-content select="[appOverflowRest]"></ng-content>
<ng-content select="[appOverflowSuffix]"></ng-content>`,
providers: [NzResizeObserver],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NzOverflowContainerComponent implements OnInit, AfterContentInit, OnDestroy {
contentInit$ = new Subject<void>();
@ContentChildren(NzOverflowItemDirective)
overflowItems: QueryList<NzOverflowItemDirective> | undefined = undefined;
@ContentChild(NzOverflowSuffixDirective)
overflowSuffix: NzOverflowSuffixDirective | undefined = undefined;
@ContentChild(NzOverflowRestDirective) overflowRest: NzOverflowRestDirective | undefined = undefined;
overflowItems$ = new ReplaySubject<QueryList<NzOverflowItemDirective>>(1);
destroy$ = new Subject<void>();
containerWidth$ = this.nzResizeObserver
.observe(this.elementRef.nativeElement)
.pipe(map(([item]) => item.target.clientWidth || 0));
restWidth$ = new BehaviorSubject<number>(0);
suffixWidth$ = new BehaviorSubject<number>(0);
suffixFixedStart$ = new BehaviorSubject<number | null>(null);
displayCount$ = new BehaviorSubject<number>(Number.MAX_SAFE_INTEGER);
restReady$ = new BehaviorSubject<boolean>(false);
maxRestWith$ = this.restWidth$.pipe(
pairwise(),
map(([prevRestWidth, restWidth]) => Math.max(prevRestWidth, restWidth))
);
omittedItems$ = combineLatest([this.overflowItems$, this.displayCount$]).pipe(
withLatestFrom(this.contentInit$),
map(([[overflowItems, displayCount]]) => overflowItems.toArray().slice(displayCount + 1))
);
displayRest$ = combineLatest([this.restReady$, this.omittedItems$]).pipe(
map(([restReady, omittedItems]) => restReady && !!omittedItems.length)
);

updateDisplayCount(count: number, notReady?: boolean): void {
this.displayCount$.next(count);
if (this.overflowItems && !notReady) {
this.restReady$.next(count < this.overflowItems.length - 1);
}
}

constructor(
private nzResizeObserver: NzResizeObserver,
private elementRef: ElementRef,
private cdr: ChangeDetectorRef
) {}

ngOnInit(): void {
const overflowItemsWidth$ = this.overflowItems$.pipe(
switchMap(items => combineLatest(items.map(item => item.itemWidth$)))
) as Observable<number[]>;
this.overflowItems$.pipe(takeUntil(this.destroy$)).subscribe(overflowItems => {
if (!overflowItems.length) {
this.displayCount$.next(0);
this.suffixFixedStart$.next(null);
}
});
combineLatest([overflowItemsWidth$, this.containerWidth$, this.maxRestWith$, this.restWidth$, this.suffixWidth$])
.pipe(
filter(([, containerWidth, maxRestWith]) => !!(containerWidth && maxRestWith)),
takeUntil(this.destroy$)
)
.subscribe(([overflowItemsWidth, containerWidth, maxRestWith, restWidth, suffixWidth]) => {
let totalWidth = suffixWidth;
const len = overflowItemsWidth.length;
const lastIndex = len - 1;
for (let i = 0; i < len; i += 1) {
const currentItemWidth = overflowItemsWidth[i];
// Break since data not ready
if (currentItemWidth === undefined) {
this.updateDisplayCount(i - 1, true);
break;
} else {
// Find best match
totalWidth += currentItemWidth;

if (
// Only one means `totalWidth` is the final width
(lastIndex === 0 && totalWidth <= containerWidth) ||
// Last two width will be the final width
(i === lastIndex - 1 &&
overflowItemsWidth[lastIndex] !== undefined &&
totalWidth + overflowItemsWidth[lastIndex]! <= containerWidth)
) {
// Additional check if match the end
this.updateDisplayCount(lastIndex);
this.suffixFixedStart$.next(null);
break;
} else if (totalWidth + maxRestWith > containerWidth) {
// Can not hold all the content to show rest
this.updateDisplayCount(i - 1);
this.suffixFixedStart$.next(totalWidth - currentItemWidth - suffixWidth + restWidth);
break;
}
this.cdr.detectChanges();
}
}
if (
this.overflowSuffix &&
overflowItemsWidth[0] !== undefined &&
overflowItemsWidth[0] + suffixWidth > containerWidth
) {
this.suffixFixedStart$.next(null);
}

this.cdr.detectChanges();
});
combineLatest([this.suffixFixedStart$, this.displayCount$])
.pipe(takeUntil(this.destroy$))
.subscribe(([suffixFixedStart, displayCount]) => {
this.overflowSuffix?.setSuffixStyle(suffixFixedStart, displayCount);
});
combineLatest([this.displayCount$, this.overflowItems$])
.pipe(takeUntil(this.destroy$))
.subscribe(([displayCount, overflowItems]) =>
overflowItems.forEach((item, index) => item.setItemStyle(index <= displayCount, index))
);
combineLatest([this.displayRest$, this.displayCount$])
.pipe(takeUntil(this.destroy$))
.subscribe(([displayRest, displayCount]) => {
this.overflowRest?.setRestStyle(displayRest, displayRest ? displayCount : Number.MAX_SAFE_INTEGER);
});
}
ngAfterContentInit(): void {
this.overflowItems?.changes.pipe(startWith(this.overflowItems)).subscribe(this.overflowItems$);
this.overflowSuffix?.suffixWidth$.subscribe(this.suffixWidth$);
this.overflowRest?.restWidth$.subscribe(this.restWidth$);
this.contentInit$.next();
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
46 changes: 46 additions & 0 deletions components/cdk/overflow/overflow-item.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { ChangeDetectorRef, Directive, ElementRef } from '@angular/core';
import { distinctUntilChanged, map, startWith, tap } from 'rxjs/operators';

import { NzResizeObserver } from 'ng-zorro-antd/cdk/resize-observer';

@Directive({
selector: '[nzOverflowItem]',
host: {
'[style]': 'overflowStyle'
}
})
export class NzOverflowItemDirective {
overflowStyle: { [key: string]: string | number | undefined } | undefined = undefined;
itemWidth$ = this.nzResizeObserver.observe(this.elementRef.nativeElement).pipe(
map(([item]) => (item.target as HTMLElement).offsetWidth),
distinctUntilChanged(),
startWith(undefined),
tap(width => {
this.itemWidth = width;
})
);
itemWidth: number | undefined = undefined;
constructor(
private nzResizeObserver: NzResizeObserver,
public elementRef: ElementRef,
private cdr: ChangeDetectorRef
) {}

setItemStyle(display: boolean, order: number): void {
const mergedHidden = !display;
this.overflowStyle = {
opacity: mergedHidden ? 0 : 1,
height: mergedHidden ? 0 : undefined,
overflowY: mergedHidden ? 'hidden' : undefined,
order: order,
pointerEvents: mergedHidden ? 'none' : undefined,
position: mergedHidden ? 'absolute' : undefined
};
this.cdr.detectChanges();
}
}
43 changes: 43 additions & 0 deletions components/cdk/overflow/overflow-rest.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { ChangeDetectorRef, Directive, ElementRef } from '@angular/core';
import { map, startWith, tap } from 'rxjs/operators';

import { NzResizeObserver } from 'ng-zorro-antd/cdk/resize-observer';

@Directive({
selector: '[nzOverflowRest]',
host: {
'[style]': 'restStyle'
}
})
export class NzOverflowRestDirective {
restStyle: { [key: string]: string | number | undefined } | undefined = undefined;
restWidth$ = this.nzResizeObserver.observe(this.elementRef.nativeElement).pipe(
map(([item]) => (item.target as HTMLElement).offsetWidth),
startWith(0),
tap(width => (this.restWidth = width))
);
restWidth = 0;
constructor(
private nzResizeObserver: NzResizeObserver,
private elementRef: ElementRef,
private cdr: ChangeDetectorRef
) {}

setRestStyle(display: boolean, order: number): void {
const mergedHidden = !display;
this.restStyle = {
opacity: mergedHidden ? 0 : 1,
height: mergedHidden ? 0 : undefined,
overflowY: mergedHidden ? 'hidden' : undefined,
order: order,
pointerEvents: mergedHidden ? 'none' : undefined,
position: mergedHidden ? 'absolute' : undefined
};
this.cdr.detectChanges();
}
}
47 changes: 47 additions & 0 deletions components/cdk/overflow/overflow-suffix.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { ChangeDetectorRef, Directive, ElementRef } from '@angular/core';
import { map, tap } from 'rxjs/operators';

import { NzResizeObserver } from 'ng-zorro-antd/cdk/resize-observer';

@Directive({
selector: '[nzOverflowSuffix]',
host: {
'[style]': 'suffixStyle'
}
})
export class NzOverflowSuffixDirective {
suffixStyle = {};
suffixWidth$ = this.nzResizeObserver.observe(this.elementRef.nativeElement).pipe(
map(([item]) => (item.target as HTMLElement).offsetWidth),
tap(width => (this.suffixWidth = width))
);
suffixWidth = 0;
constructor(
private nzResizeObserver: NzResizeObserver,
private elementRef: ElementRef,
private cdr: ChangeDetectorRef
) {}

setSuffixStyle(start: number | null, order: number): void {
if (start !== null) {
this.suffixStyle = {
position: 'absolute',
left: `${start}px`,
top: 0,
opacity: 1,
order: order
};
} else {
this.suffixStyle = {
opacity: 1,
order: order
};
}
this.cdr.detectChanges();
}
}
25 changes: 25 additions & 0 deletions components/cdk/overflow/overflow.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { NgModule } from '@angular/core';

import { NzResizeObserverModule } from 'ng-zorro-antd/cdk/resize-observer';

import { NzOverflowContainerComponent } from './overflow-container.component';
import { NzOverflowItemDirective } from './overflow-item.directive';
import { NzOverflowRestDirective } from './overflow-rest.directive';
import { NzOverflowSuffixDirective } from './overflow-suffix.directive';

@NgModule({
imports: [NzResizeObserverModule],
declarations: [
NzOverflowContainerComponent,
NzOverflowItemDirective,
NzOverflowRestDirective,
NzOverflowSuffixDirective
],
exports: [NzOverflowContainerComponent, NzOverflowItemDirective, NzOverflowRestDirective, NzOverflowSuffixDirective]
})
export class NzOverflowModule {}
7 changes: 7 additions & 0 deletions components/cdk/overflow/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ngPackage": {
"lib": {
"entryFile": "public-api.ts"
}
}
}
10 changes: 10 additions & 0 deletions components/cdk/overflow/public-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export { NzOverflowModule } from './overflow.module';
export { NzOverflowContainerComponent } from './overflow-container.component';
export { NzOverflowItemDirective } from './overflow-item.directive';
export { NzOverflowRestDirective } from './overflow-rest.directive';
export { NzOverflowSuffixDirective } from './overflow-suffix.directive';
6 changes: 6 additions & 0 deletions components/cdk/resize-observer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export * from './public-api';
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export { NzResizeObserversModule } from './resize-observers.module';
export { NzResizeObserver, NzResizeObserverFactory as ɵNzResizeObserverFactory } from './resize-observers.service';
export { NzResizeObserverModule } from './resize-observer.module';
export { NzResizeObserverDirective } from './resize-observer.directive';
export { NzResizeObserver, NzResizeObserverFactory as ɵNzResizeObserverFactory } from './resize-observer.service';
Loading

0 comments on commit edd410a

Please sign in to comment.