mirror of https://github.com/abpframework/abp.git
committed by
GitHub
58 changed files with 1267 additions and 1042 deletions
@ -1,119 +1,197 @@ |
|||||
@if (isBrowser) { |
@if (isBrowser) { |
||||
<ngx-datatable #table default [rows]="data" [count]="recordsTotal" [list]="list" |
<ngx-datatable |
||||
[selectionType]="selectable ? _selectionType : undefined" (activate)="tableActivate.emit($event)" |
#table |
||||
(select)="onSelect($event)" [selected]="selected" (scroll)="onScroll($event)" [scrollbarV]="infiniteScroll" |
default |
||||
[style.height]="getTableHeight()" [loadingIndicator]="infiniteScroll && isLoading" |
[rows]="data" |
||||
[footerHeight]="infiniteScroll ? false : 50"> |
[count]="recordsTotal()" |
||||
@if (effectiveRowDetailTemplate) { |
[list]="list()" |
||||
<ngx-datatable-row-detail [rowHeight]="effectiveRowDetailHeight"> |
[selectionType]="selectable() ? selectionType() : undefined" |
||||
<ng-template let-row="row" let-expanded="expanded" ngx-datatable-row-detail-template> |
(activate)="tableActivate.emit($event)" |
||||
<ng-container |
(select)="onSelect($event)" |
||||
*ngTemplateOutlet="effectiveRowDetailTemplate; context: { row: row, expanded: expanded }" /> |
[selected]="selected()" |
||||
</ng-template> |
(scroll)="onScroll($event)" |
||||
</ngx-datatable-row-detail> |
[scrollbarV]="infiniteScroll()" |
||||
|
[style.height]="getTableHeight()" |
||||
|
[loadingIndicator]="infiniteScroll() && isLoading()" |
||||
|
[footerHeight]="infiniteScroll() ? false : 50" |
||||
|
> |
||||
|
@if (effectiveRowDetailTemplate) { |
||||
|
<ngx-datatable-row-detail [rowHeight]="effectiveRowDetailHeight"> |
||||
|
<ng-template let-row="row" let-expanded="expanded" ngx-datatable-row-detail-template> |
||||
|
<ng-container |
||||
|
[ngTemplateOutlet]="effectiveRowDetailTemplate" |
||||
|
[ngTemplateOutletContext]="{ row: row, expanded: expanded }" |
||||
|
></ng-container> |
||||
|
</ng-template> |
||||
|
</ngx-datatable-row-detail> |
||||
|
|
||||
<ngx-datatable-column [width]="50" [resizeable]="false" [sortable]="false" [draggable]="false" |
<ngx-datatable-column |
||||
[canAutoResize]="false"> |
[width]="50" |
||||
<ng-template let-row="row" let-expanded="expanded" ngx-datatable-cell-template> |
[resizeable]="false" |
||||
<button type="button" class="btn btn-link text-decoration-none text-muted p-0" |
[sortable]="false" |
||||
[attr.aria-label]="expanded ? 'Collapse' : 'Expand'" (click)="toggleExpandRow(row)"> |
[draggable]="false" |
||||
<i class="fa" [class.fa-chevron-down]="!expanded" [class.fa-chevron-up]="expanded"></i> |
[canAutoResize]="false" |
||||
</button> |
> |
||||
</ng-template> |
<ng-template let-row="row" let-expanded="expanded" ngx-datatable-cell-template> |
||||
</ngx-datatable-column> |
<button |
||||
} |
type="button" |
||||
@if(selectable) { |
class="btn btn-link text-decoration-none text-muted p-0" |
||||
<ngx-datatable-column [width]="50" [sortable]="false" [canAutoResize]="false" [draggable]="false" |
[attr.aria-label]="expanded ? 'Collapse' : 'Expand'" |
||||
[resizeable]="false"> |
(click)="toggleExpandRow(row)" |
||||
|
> |
||||
<ng-template ngx-datatable-header-template let-value="value" let-allRowsSelected="allRowsSelected" |
<i class="fa" [class.fa-chevron-down]="!expanded" [class.fa-chevron-up]="expanded"></i> |
||||
let-selectFn="selectFn"> |
</button> |
||||
@if (_selectionType !== 'single') { |
</ng-template> |
||||
<div class="form-check"> |
</ngx-datatable-column> |
||||
<input class="form-check-input table-check" type="checkbox" [checked]="allRowsSelected" |
} |
||||
(change)="selectFn(!allRowsSelected)" /> |
@if (selectable()) { |
||||
</div> |
<ngx-datatable-column |
||||
} |
[width]="50" |
||||
</ng-template> |
[sortable]="false" |
||||
|
[canAutoResize]="false" |
||||
<ng-template ngx-datatable-cell-template let-value="value" let-row="row" let-isSelected="isSelected" |
[draggable]="false" |
||||
let-onCheckboxChangeFn="onCheckboxChangeFn"> |
[resizeable]="false" |
||||
@if(_selectionType === 'single') { |
> |
||||
<div class="h-100 form-check form-check-sm form-check-custom form-check-solid"> |
<ng-template |
||||
<input class="form-check-input" type="radio" [checked]="isSelected" (change)="onCheckboxChangeFn($event)" /> |
ngx-datatable-header-template |
||||
</div> |
let-value="value" |
||||
} |
let-allRowsSelected="allRowsSelected" |
||||
@if (_selectionType !== 'single') { |
let-selectFn="selectFn" |
||||
<div class="h-100 form-check form-check-sm form-check-custom form-check-solid"> |
> |
||||
<input class="form-check-input" type="checkbox" [checked]="isSelected" (change)="onCheckboxChangeFn($event)" /> |
@if (selectionType() !== 'single') { |
||||
</div> |
<div class="form-check"> |
||||
} |
<input |
||||
</ng-template> |
class="form-check-input table-check" |
||||
|
type="checkbox" |
||||
|
[checked]="allRowsSelected" |
||||
|
(change)="selectFn(!allRowsSelected)" |
||||
|
/> |
||||
|
</div> |
||||
|
} |
||||
|
</ng-template> |
||||
|
|
||||
</ngx-datatable-column> |
<ng-template |
||||
} |
ngx-datatable-cell-template |
||||
@if (actionsTemplate || (actionList.length && hasAtLeastOnePermittedAction)) { |
let-value="value" |
||||
<ngx-datatable-column [name]="actionsText | abpLocalization" [maxWidth]="_actionsColumnWidth() ?? undefined" |
let-row="row" |
||||
[width]="_actionsColumnWidth() ?? 200" [canAutoResize]="!_actionsColumnWidth()" [sortable]="false"> |
let-isSelected="isSelected" |
||||
<ng-template let-row="row" let-i="rowIndex" ngx-datatable-cell-template> |
let-onCheckboxChangeFn="onCheckboxChangeFn" |
||||
<ng-container |
> |
||||
*ngTemplateOutlet="actionsTemplate || gridActions; context: { $implicit: row, index: i }"></ng-container> |
@if (selectionType() === 'single') { |
||||
<ng-template #gridActions> |
<div class="h-100 form-check form-check-sm form-check-custom form-check-solid"> |
||||
@if (isVisibleActions(row)) { |
<input |
||||
<abp-grid-actions [index]="i" [record]="row" text="AbpUi::Actions"></abp-grid-actions> |
class="form-check-input" |
||||
} |
type="radio" |
||||
</ng-template> |
[checked]="isSelected" |
||||
</ng-template> |
(change)="onCheckboxChangeFn($event)" |
||||
</ngx-datatable-column> |
/> |
||||
} |
</div> |
||||
@for (prop of propList; track prop.name; let i = $index) { |
} |
||||
<ngx-datatable-column *abpVisible="prop.columnVisible(getInjected)" [width]="columnWidths[i] ?? 200" |
@if (selectionType() !== 'single') { |
||||
[canAutoResize]="!columnWidths[i]" |
<div class="h-100 form-check form-check-sm form-check-custom form-check-solid"> |
||||
[name]="(prop.isExtra ? '::' + prop.displayName : prop.displayName) | abpLocalization" [prop]="prop.name" |
<input |
||||
[sortable]="prop.sortable"> |
class="form-check-input" |
||||
<ng-template ngx-datatable-header-template let-column="column" let-sortFn="sortFn"> |
type="checkbox" |
||||
@if (prop.tooltip) { |
[checked]="isSelected" |
||||
<span [ngbTooltip]="prop.tooltip.text | abpLocalization" [placement]="prop.tooltip.placement || 'auto'" |
(change)="onCheckboxChangeFn($event)" |
||||
container="body" [class.pointer]="prop.sortable" (click)="prop.sortable && sortFn(column)"> |
/> |
||||
{{ column.name }} <i class="fa fa-info-circle" aria-hidden="true"></i> |
</div> |
||||
</span> |
} |
||||
} @else { |
</ng-template> |
||||
<span [class.pointer]="prop.sortable" (click)="prop.sortable && sortFn(column)"> |
</ngx-datatable-column> |
||||
{{ column.name }} |
} |
||||
</span> |
@if (actionsTemplate() || (actionList.length && hasAtLeastOnePermittedAction)) { |
||||
} |
<ngx-datatable-column |
||||
</ng-template> |
[name]="actionsText() | abpLocalization" |
||||
<ng-template let-row="row" let-i="index" ngx-datatable-cell-template> |
[maxWidth]="_actionsColumnWidth() ?? undefined" |
||||
<ng-container *abpPermission="prop.permission; runChangeDetection: false"> |
[width]="_actionsColumnWidth() ?? 200" |
||||
<ng-container *abpVisible="row['_' + prop.name]?.visible"> |
[canAutoResize]="!_actionsColumnWidth()" |
||||
@if (!row['_' + prop.name].component) { |
[sortable]="false" |
||||
@if (prop.type === 'datetime' || prop.type === 'date' || prop.type === 'time') { |
> |
||||
<div [innerHTML]=" |
<ng-template let-row="row" let-i="rowIndex" ngx-datatable-cell-template> |
||||
!prop.isExtra |
@if (actionsTemplate(); as template) { |
||||
? (row['_' + prop.name]?.value | async | abpUtcToLocal:prop.type) |
<ng-container |
||||
: ('::' + (row['_' + prop.name]?.value | async | abpUtcToLocal:prop.type) | abpLocalization) |
[ngTemplateOutlet]="template" |
||||
" (click)=" |
[ngTemplateOutletContext]="{ $implicit: row, index: i }" |
||||
prop.action && prop.action({ getInjected: getInjected, record: row, index: i }) |
></ng-container> |
||||
" [class]="entityPropTypeClasses[prop.type]" [class.pointer]="prop.action"></div> |
} @else if (isVisibleActions(row)) { |
||||
} @else { |
<abp-grid-actions [index]="i" [record]="row" text="AbpUi::Actions"></abp-grid-actions> |
||||
<div [innerHTML]=" |
|
||||
!prop.isExtra |
|
||||
? (row['_' + prop.name]?.value | async) |
|
||||
: ('::' + (row['_' + prop.name]?.value | async) | abpLocalization) |
|
||||
" (click)=" |
|
||||
prop.action && prop.action({ getInjected: getInjected, record: row, index: i }) |
|
||||
" [class]="entityPropTypeClasses[prop.type]" [class.pointer]="prop.action"></div> |
|
||||
} |
} |
||||
|
</ng-template> |
||||
|
</ngx-datatable-column> |
||||
|
} |
||||
|
@for (prop of propList; track prop.name; let i = $index) { |
||||
|
<ngx-datatable-column |
||||
|
*abpVisible="prop.columnVisible(getInjected)" |
||||
|
[width]="columnWidths()[i] ?? 200" |
||||
|
[canAutoResize]="!columnWidths()[i]" |
||||
|
[name]="(prop.isExtra ? '::' + prop.displayName : prop.displayName) | abpLocalization" |
||||
|
[prop]="prop.name" |
||||
|
[sortable]="prop.sortable" |
||||
|
> |
||||
|
<ng-template ngx-datatable-header-template let-column="column" let-sortFn="sortFn"> |
||||
|
@if (prop.tooltip) { |
||||
|
<span |
||||
|
[ngbTooltip]="prop.tooltip.text | abpLocalization" |
||||
|
[placement]="prop.tooltip.placement || 'auto'" |
||||
|
container="body" |
||||
|
[class.pointer]="prop.sortable" |
||||
|
(click)="prop.sortable && sortFn(column)" |
||||
|
> |
||||
|
{{ column.name }} <i class="fa fa-info-circle" aria-hidden="true"></i> |
||||
|
</span> |
||||
} @else { |
} @else { |
||||
<ng-container *ngComponentOutlet=" |
<span [class.pointer]="prop.sortable" (click)="prop.sortable && sortFn(column)"> |
||||
row['_' + prop.name].component; |
{{ column.name }} |
||||
injector: row['_' + prop.name].injector |
</span> |
||||
"></ng-container> |
|
||||
} |
} |
||||
</ng-container> |
</ng-template> |
||||
</ng-container> |
<ng-template let-row="row" let-i="index" ngx-datatable-cell-template> |
||||
</ng-template> |
<ng-container *abpPermission="prop.permission; runChangeDetection: false"> |
||||
</ngx-datatable-column> |
<ng-container *abpVisible="row['_' + prop.name]?.visible"> |
||||
} |
@if (!row['_' + prop.name].component) { |
||||
</ngx-datatable> |
@if (prop.type === 'datetime' || prop.type === 'date' || prop.type === 'time') { |
||||
} |
<div |
||||
|
[innerHTML]=" |
||||
|
!prop.isExtra |
||||
|
? (row['_' + prop.name]?.value | async | abpUtcToLocal: prop.type) |
||||
|
: ('::' + (row['_' + prop.name]?.value | async | abpUtcToLocal: prop.type) |
||||
|
| abpLocalization) |
||||
|
" |
||||
|
(click)=" |
||||
|
prop.action && |
||||
|
prop.action({ getInjected: getInjected, record: row, index: i }) |
||||
|
" |
||||
|
[class]="entityPropTypeClasses[prop.type]" |
||||
|
[class.pointer]="prop.action" |
||||
|
></div> |
||||
|
} @else { |
||||
|
<div |
||||
|
[innerHTML]=" |
||||
|
!prop.isExtra |
||||
|
? (row['_' + prop.name]?.value | async) |
||||
|
: ('::' + (row['_' + prop.name]?.value | async) | abpLocalization) |
||||
|
" |
||||
|
(click)=" |
||||
|
prop.action && |
||||
|
prop.action({ getInjected: getInjected, record: row, index: i }) |
||||
|
" |
||||
|
[class]="entityPropTypeClasses[prop.type]" |
||||
|
[class.pointer]="prop.action" |
||||
|
></div> |
||||
|
} |
||||
|
} @else { |
||||
|
<ng-container |
||||
|
*ngComponentOutlet=" |
||||
|
row['_' + prop.name].component; |
||||
|
injector: row['_' + prop.name].injector |
||||
|
" |
||||
|
></ng-container> |
||||
|
} |
||||
|
</ng-container> |
||||
|
</ng-container> |
||||
|
</ng-template> |
||||
|
</ngx-datatable-column> |
||||
|
} |
||||
|
</ngx-datatable> |
||||
|
} |
||||
|
|||||
@ -1,13 +1,13 @@ |
|||||
import { Component, HostBinding, Input } from '@angular/core'; |
import { Component, HostBinding, input } from '@angular/core'; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'abp-card-body', |
selector: 'abp-card-body', |
||||
template: ` <div [class]="cardBodyClass" [style]="cardBodyStyle">
|
template: ` <div [class]="cardBodyClass()" [style]="cardBodyStyle()">
|
||||
<ng-content></ng-content> |
<ng-content></ng-content> |
||||
</div>`,
|
</div>`,
|
||||
}) |
}) |
||||
export class CardBodyComponent { |
export class CardBodyComponent { |
||||
@HostBinding('class') componentClass = 'card-body'; |
@HostBinding('class') componentClass = 'card-body'; |
||||
@Input() cardBodyClass: string; |
readonly cardBodyClass = input<string>(undefined); |
||||
@Input() cardBodyStyle: string; |
readonly cardBodyStyle = input<string>(undefined); |
||||
} |
} |
||||
|
|||||
@ -1,14 +1,14 @@ |
|||||
import { Component, Input } from '@angular/core'; |
import { Component, input } from '@angular/core'; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'abp-card', |
selector: 'abp-card', |
||||
template: ` <div class="card" [class]="cardClass" [style]="cardStyle">
|
template: ` <div class="card" [class]="cardClass()" [style]="cardStyle()">
|
||||
<ng-content></ng-content> |
<ng-content></ng-content> |
||||
</div>`,
|
</div>`,
|
||||
imports: [], |
imports: [], |
||||
}) |
}) |
||||
export class CardComponent { |
export class CardComponent { |
||||
@Input() cardClass: string; |
readonly cardClass = input<string>(undefined); |
||||
|
|
||||
@Input() cardStyle: string; |
readonly cardStyle = input<string>(undefined); |
||||
} |
} |
||||
|
|||||
@ -1,19 +1,19 @@ |
|||||
import { Directive, Input, OnChanges, SimpleChanges, inject } from '@angular/core'; |
import { Directive, effect, inject, input } from '@angular/core'; |
||||
import { NgControl } from '@angular/forms'; |
import { NgControl } from '@angular/forms'; |
||||
|
|
||||
@Directive({ |
@Directive({ |
||||
selector: '[abpDisabled]', |
selector: '[abpDisabled]', |
||||
}) |
}) |
||||
export class DisabledDirective implements OnChanges { |
export class DisabledDirective { |
||||
private ngControl = inject(NgControl, { host: true }); |
private ngControl = inject(NgControl, { host: true }); |
||||
|
|
||||
@Input() |
readonly abpDisabled = input(false); |
||||
abpDisabled = false; |
|
||||
|
|
||||
// Related issue: https://github.com/angular/angular/issues/35330
|
// Related issue: https://github.com/angular/angular/issues/35330
|
||||
ngOnChanges({ abpDisabled }: SimpleChanges) { |
private disabledEffect = effect(() => { |
||||
if (this.ngControl.control && abpDisabled) { |
const disabled = this.abpDisabled(); |
||||
this.ngControl.control[abpDisabled.currentValue ? 'disable' : 'enable'](); |
if (this.ngControl.control) { |
||||
|
this.ngControl.control[disabled ? 'disable' : 'enable'](); |
||||
} |
} |
||||
} |
}); |
||||
} |
} |
||||
|
|||||
@ -1,47 +1,48 @@ |
|||||
import { |
import { |
||||
AfterViewInit, |
AfterViewInit, |
||||
ChangeDetectorRef, |
ChangeDetectorRef, |
||||
Directive, |
computed, |
||||
ElementRef, |
Directive, |
||||
HostBinding, |
ElementRef, |
||||
Input, |
inject, |
||||
inject |
input, |
||||
|
signal |
||||
} from '@angular/core'; |
} from '@angular/core'; |
||||
|
|
||||
@Directive({ |
@Directive({ |
||||
selector: '[abpEllipsis]', |
selector: '[abpEllipsis]', |
||||
|
host: { |
||||
|
'[title]': 'effectiveTitle()', |
||||
|
'[class.abp-ellipsis-inline]': 'inlineClass()', |
||||
|
'[class.abp-ellipsis]': 'ellipsisClass()', |
||||
|
'[style.max-width]': 'maxWidth()' |
||||
|
} |
||||
}) |
}) |
||||
export class EllipsisDirective implements AfterViewInit { |
export class EllipsisDirective implements AfterViewInit { |
||||
private cdRef = inject(ChangeDetectorRef); |
private cdRef = inject(ChangeDetectorRef); |
||||
private elRef = inject(ElementRef); |
private elRef = inject(ElementRef); |
||||
|
|
||||
@Input('abpEllipsis') |
readonly width = input<string | undefined>(undefined, { alias: 'abpEllipsis' }); |
||||
width?: string; |
readonly title = input<string | undefined>(undefined); |
||||
|
readonly enabled = input(true, { alias: 'abpEllipsisEnabled' }); |
||||
|
|
||||
@HostBinding('title') |
private readonly autoTitle = signal<string | undefined>(undefined); |
||||
@Input() |
|
||||
title?: string; |
|
||||
|
|
||||
@Input('abpEllipsisEnabled') |
protected readonly effectiveTitle = computed(() => this.title() || this.autoTitle()); |
||||
enabled = true; |
|
||||
|
|
||||
@HostBinding('class.abp-ellipsis-inline') |
protected readonly inlineClass = computed(() => this.enabled() && !!this.width()); |
||||
get inlineClass() { |
|
||||
return this.enabled && this.width; |
|
||||
} |
|
||||
|
|
||||
@HostBinding('class.abp-ellipsis') |
protected readonly ellipsisClass = computed(() => this.enabled() && !this.width()); |
||||
get class() { |
|
||||
return this.enabled && !this.width; |
|
||||
} |
|
||||
|
|
||||
@HostBinding('style.max-width') |
protected readonly maxWidth = computed(() => { |
||||
get maxWidth() { |
const width = this.width(); |
||||
return this.enabled && this.width ? this.width || '170px' : undefined; |
return this.enabled() && width ? width || '170px' : undefined; |
||||
} |
}); |
||||
|
|
||||
ngAfterViewInit() { |
ngAfterViewInit() { |
||||
this.title = this.title || (this.elRef.nativeElement as HTMLElement).innerText; |
if (!this.title()) { |
||||
this.cdRef.detectChanges(); |
this.autoTitle.set((this.elRef.nativeElement as HTMLElement).innerText); |
||||
|
this.cdRef.detectChanges(); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue