-
Notifications
You must be signed in to change notification settings - Fork 674
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(abc:cell): add
cell
component (#1530)
- Loading branch information
Showing
37 changed files
with
2,031 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Directive, Input, OnInit, Type, ViewContainerRef } from '@angular/core'; | ||
|
||
import { warn } from '@delon/util/other'; | ||
|
||
import { CellService } from './cell.service'; | ||
import { CellWidgetData } from './cell.types'; | ||
|
||
@Directive({ | ||
selector: '[cell-widget-host]' | ||
}) | ||
export class CellHostDirective implements OnInit { | ||
@Input() data!: CellWidgetData; | ||
|
||
constructor( | ||
private srv: CellService, | ||
private viewContainerRef: ViewContainerRef | ||
) {} | ||
|
||
ngOnInit(): void { | ||
const widget = this.data.options!.widget!; | ||
const componentType = this.srv.getWidget(widget.key!)?.ref as Type<unknown>; | ||
if (componentType == null) { | ||
if (typeof ngDevMode === 'undefined' || ngDevMode) { | ||
warn(`cell: No widget for type "${widget.key}"`); | ||
} | ||
return; | ||
} | ||
|
||
this.viewContainerRef.clear(); | ||
const componentRef = this.viewContainerRef.createComponent(componentType); | ||
(componentRef.instance as { data: CellWidgetData }).data = this.data; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
import { | ||
ChangeDetectionStrategy, | ||
ChangeDetectorRef, | ||
Component, | ||
ElementRef, | ||
EventEmitter, | ||
Inject, | ||
Input, | ||
OnChanges, | ||
OnDestroy, | ||
Output, | ||
Renderer2, | ||
SimpleChange, | ||
ViewEncapsulation | ||
} from '@angular/core'; | ||
import type { SafeValue } from '@angular/platform-browser'; | ||
import { Router } from '@angular/router'; | ||
import { Subscription } from 'rxjs'; | ||
|
||
import { updateHostClass } from '@delon/util/browser'; | ||
import { BooleanInput, InputBoolean } from '@delon/util/decorator'; | ||
import { WINDOW } from '@delon/util/token'; | ||
import type { NzSafeAny } from 'ng-zorro-antd/core/types'; | ||
import { NzImage, NzImageService } from 'ng-zorro-antd/image'; | ||
|
||
import { CellService } from './cell.service'; | ||
import type { CellDefaultText, CellOptions, CellTextResult, CellValue, CellWidgetData } from './cell.types'; | ||
|
||
@Component({ | ||
selector: 'cell, [cell]', | ||
template: ` | ||
<ng-template #text> | ||
<ng-container [ngSwitch]="safeOpt.type"> | ||
<label | ||
*ngSwitchCase="'checkbox'" | ||
nz-checkbox | ||
[nzDisabled]="disabled" | ||
[ngModel]="value" | ||
(ngModelChange)="change($event)" | ||
> | ||
{{ safeOpt.checkbox?.label }} | ||
</label> | ||
<label | ||
*ngSwitchCase="'radio'" | ||
nz-radio | ||
[nzDisabled]="disabled" | ||
[ngModel]="value" | ||
(ngModelChange)="change($event)" | ||
> | ||
{{ safeOpt.radio?.label }} | ||
</label> | ||
<a | ||
*ngSwitchCase="'link'" | ||
(click)="_link($event)" | ||
[attr.target]="safeOpt.link?.target" | ||
[attr.title]="value" | ||
[innerHTML]="_text" | ||
></a> | ||
<nz-tag *ngSwitchCase="'tag'" [nzColor]="res?.result?.color"> | ||
<span [innerHTML]="_text"></span> | ||
</nz-tag> | ||
<nz-badge *ngSwitchCase="'badge'" [nzStatus]="res?.result?.color" nzText="{{ _text }}" /> | ||
<ng-template *ngSwitchCase="'widget'" cell-widget-host [data]="hostData" /> | ||
<ng-container *ngSwitchCase="'img'"> | ||
<img | ||
*ngFor="let i of $any(_text)" | ||
[attr.src]="i" | ||
[attr.height]="safeOpt.img?.size" | ||
[attr.width]="safeOpt.img?.size" | ||
(click)="_showImg(i)" | ||
class="img" | ||
[class.point]="safeOpt.img?.big" | ||
/> | ||
</ng-container> | ||
<ng-container *ngSwitchDefault> | ||
<span *ngIf="!isText" [innerHTML]="_text" [attr.title]="value"></span> | ||
<span *ngIf="isText" [innerText]="_text" [attr.title]="value"></span> | ||
<span *ngIf="_unit" class="unit">{{ _unit }}</span> | ||
</ng-container> | ||
</ng-container> | ||
</ng-template> | ||
<ng-template #textWrap> | ||
<ng-container *ngIf="showDefault">{{ safeOpt.default?.text }}</ng-container> | ||
<ng-container *ngIf="!showDefault"> | ||
<span *ngIf="safeOpt.tooltip; else text" [nz-tooltip]="safeOpt.tooltip"> | ||
<ng-template [ngTemplateOutlet]="text" /> | ||
</span> | ||
</ng-container> | ||
</ng-template> | ||
<span *ngIf="loading; else textWrap" nz-icon nzType="loading"></span> | ||
`, | ||
exportAs: 'cell', | ||
preserveWhitespaces: false, | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
encapsulation: ViewEncapsulation.None | ||
}) | ||
export class CellComponent implements OnChanges, OnDestroy { | ||
static ngAcceptInputType_loading: BooleanInput; | ||
static ngAcceptInputType_disabled: BooleanInput; | ||
|
||
private destroy$?: Subscription; | ||
|
||
_text!: string | SafeValue | string[] | number; | ||
_unit?: string; | ||
res?: CellTextResult; | ||
showDefault = false; | ||
|
||
@Input() value?: CellValue; | ||
@Output() readonly valueChange = new EventEmitter<NzSafeAny>(); | ||
@Input() options?: CellOptions; | ||
@Input() @InputBoolean() loading = false; | ||
@Input() @InputBoolean() disabled = false; | ||
|
||
get safeOpt(): CellOptions { | ||
return this.res?.options ?? {}; | ||
} | ||
|
||
get isText(): boolean { | ||
return this.res?.safeHtml === 'text'; | ||
} | ||
|
||
get hostData(): CellWidgetData { | ||
return { | ||
value: this.value, | ||
options: this.srv.fixOptions(this.options) | ||
}; | ||
} | ||
|
||
constructor( | ||
private srv: CellService, | ||
private router: Router, | ||
private cdr: ChangeDetectorRef, | ||
private el: ElementRef<HTMLElement>, | ||
private renderer: Renderer2, | ||
private imgSrv: NzImageService, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@Inject(WINDOW) private win: any | ||
) {} | ||
|
||
private updateValue(): void { | ||
this.destroy$?.unsubscribe(); | ||
this.destroy$ = this.srv.get(this.value, this.options).subscribe(res => { | ||
this.res = res; | ||
this.showDefault = this.value == (this.safeOpt.default as CellDefaultText).condition; | ||
this._text = res.result?.text ?? ''; | ||
this._unit = res.result?.unit ?? this.safeOpt?.unit; | ||
this.cdr.detectChanges(); | ||
this.setClass(); | ||
}); | ||
} | ||
|
||
private setClass(): void { | ||
const { el, renderer } = this; | ||
const { renderType, size } = this.safeOpt; | ||
updateHostClass(el.nativeElement, renderer, { | ||
[`cell`]: true, | ||
[`cell__${renderType}`]: renderType != null, | ||
[`cell__${size}`]: size != null, | ||
[`cell__has-unit`]: this._unit, | ||
[`cell__has-default`]: this.showDefault, | ||
[`cell__disabled`]: this.disabled | ||
}); | ||
el.nativeElement.dataset.type = this.safeOpt.type; | ||
} | ||
|
||
ngOnChanges(changes: { [p in keyof CellComponent]?: SimpleChange }): void { | ||
// Do not call updateValue when only updating loading, disabled | ||
if (Object.keys(changes).every(k => ['loading', 'disabled'].includes(k))) { | ||
this.setClass(); | ||
} else { | ||
this.updateValue(); | ||
} | ||
} | ||
|
||
change(value: NzSafeAny): void { | ||
this.value = value; | ||
this.valueChange.emit(value); | ||
} | ||
|
||
_link(e: Event): void { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
|
||
if (this.disabled) return; | ||
|
||
const link = this.safeOpt.link; | ||
const url = link?.url; | ||
if (url == null) return; | ||
|
||
if (/https?:\/\//g.test(url)) { | ||
(this.win as Window).open(url, link?.target); | ||
} else { | ||
this.router.navigateByUrl(url); | ||
} | ||
} | ||
|
||
_showImg(img: string): void { | ||
const config = this.safeOpt.img; | ||
if (config == null || config.big == null) return; | ||
|
||
let idx = -1; | ||
const list = (this._text as string[]).map((p, index) => { | ||
if (idx === -1 && p === img) idx = index; | ||
return typeof config.big === 'function' ? config.big(p) : p; | ||
}); | ||
this.imgSrv | ||
.preview( | ||
list.map(p => ({ src: p }) as NzImage), | ||
config.previewOptions | ||
) | ||
.switchTo(idx); | ||
} | ||
|
||
ngOnDestroy(): void { | ||
this.destroy$?.unsubscribe(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { CommonModule } from '@angular/common'; | ||
import { NgModule } from '@angular/core'; | ||
import { FormsModule } from '@angular/forms'; | ||
|
||
import { NzBadgeModule } from 'ng-zorro-antd/badge'; | ||
import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; | ||
import { NzImageModule } from 'ng-zorro-antd/experimental/image'; | ||
import { NzIconModule } from 'ng-zorro-antd/icon'; | ||
import { NzRadioModule } from 'ng-zorro-antd/radio'; | ||
import { NzTagModule } from 'ng-zorro-antd/tag'; | ||
import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; | ||
|
||
import { CellHostDirective } from './cell-host.directive'; | ||
import { CellComponent } from './cell.component'; | ||
|
||
const COMPS = [CellComponent]; | ||
|
||
@NgModule({ | ||
imports: [ | ||
CommonModule, | ||
FormsModule, | ||
NzCheckboxModule, | ||
NzRadioModule, | ||
NzBadgeModule, | ||
NzTagModule, | ||
NzToolTipModule, | ||
NzIconModule, | ||
NzImageModule | ||
], | ||
declarations: [...COMPS, CellHostDirective], | ||
exports: COMPS | ||
}) | ||
export class CellModule {} |
Oops, something went wrong.