Browse Source

Merge pull request #24636 from abpframework/issue-24635

Add extensible table row detail feature
pull/24694/head
Yağmur Çelik 9 months ago
committed by GitHub
parent
commit
2cc3f59f9e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      docs/en/framework/ui/angular/data-table-column-extensions.md
  2. 189
      docs/en/framework/ui/angular/extensible-table-row-detail.md
  3. BIN
      docs/en/framework/ui/angular/images/row-detail-image.png
  4. 10
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/extensible-table-row-detail.component.ts
  5. 1
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/index.ts
  6. 169
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html
  7. 36
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts
  8. 1
      npm/ng-packs/packages/components/extensible/src/lib/components/index.ts
  9. 4
      npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts
  10. 4
      npm/ng-packs/packages/components/extensible/src/lib/models/row-detail.ts
  11. 2
      npm/ng-packs/packages/components/extensible/src/public-api.ts

1
docs/en/framework/ui/angular/data-table-column-extensions.md

@ -342,4 +342,5 @@ export const identityEntityPropContributors = {
## See Also ## See Also
- [Extensible Table Row Detail](extensible-table-row-detail.md)
- [Customizing Application Modules Guide](../../architecture/modularity/extending/customizing-application-modules-guide.md) - [Customizing Application Modules Guide](../../architecture/modularity/extending/customizing-application-modules-guide.md)

189
docs/en/framework/ui/angular/extensible-table-row-detail.md

@ -0,0 +1,189 @@
```json
//[doc-seo]
{
"Description": "Learn how to add expandable row details to data tables using the Extensible Table Row Detail component in ABP Framework Angular UI."
}
```
# Extensible Table Row Detail for Angular UI
## Introduction
The `<abp-extensible-table-row-detail>` component allows you to add expandable row details to any `<abp-extensible-table>`. When users click the expand icon, additional content is revealed below the row.
<img alt="Extensible Table Row Detail Example" src="./images/row-detail-image.png" width="800px" style="max-width:100%">
## Quick Start
### Step 1. Import the Component
Import `ExtensibleTableRowDetailComponent` in your component:
```typescript
import {
ExtensibleTableComponent,
ExtensibleTableRowDetailComponent
} from '@abp/ng.components/extensible';
@Component({
// ...
imports: [
ExtensibleTableComponent,
ExtensibleTableRowDetailComponent,
],
})
export class MyComponent { }
```
### Step 2. Add Row Detail Template
Place `<abp-extensible-table-row-detail>` inside `<abp-extensible-table>` with an `ng-template`:
```html
<abp-extensible-table [data]="data.items" [recordsTotal]="data.totalCount" [list]="list">
<abp-extensible-table-row-detail>
<ng-template let-row="row" let-expanded="expanded">
<div class="p-3">
<h5>{%{{{ row.name }}}%}</h5>
<p>ID: {%{{{ row.id }}}%}</p>
<p>Status: {%{{{ row.isActive ? 'Active' : 'Inactive' }}}%}</p>
</div>
</ng-template>
</abp-extensible-table-row-detail>
</abp-extensible-table>
```
An expand/collapse chevron icon will automatically appear in the first column of each row.
## API
### ExtensibleTableRowDetailComponent
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `rowHeight` | `string` &#124; `number` | `'100%'` | Height of the expanded row detail area |
### Template Context Variables
| Variable | Type | Description |
|----------|------|-------------|
| `row` | `R` | The current row data object |
| `expanded` | `boolean` | Whether the row is currently expanded |
## Usage Examples
### Basic Example
Display additional information when a row is expanded:
```html
<abp-extensible-table [data]="data.items" [list]="list">
<abp-extensible-table-row-detail>
<ng-template let-row="row">
<div class="p-3 border rounded m-2">
<strong>Details for: {%{{{ row.name }}}%}</strong>
<pre>{%{{{ row | json }}}%}</pre>
</div>
</ng-template>
</abp-extensible-table-row-detail>
</abp-extensible-table>
```
### With Custom Row Height
Specify a fixed height for the detail area:
```html
<abp-extensible-table-row-detail [rowHeight]="200">
<ng-template let-row="row">
<div class="p-3">Fixed 200px height content</div>
</ng-template>
</abp-extensible-table-row-detail>
```
### Using Expanded State
Apply conditional styling based on expansion state:
```html
<abp-extensible-table-row-detail>
<ng-template let-row="row" let-expanded="expanded">
<div class="p-3" [class.fade-in]="expanded">
<p>This row is {%{{{ expanded ? 'expanded' : 'collapsed' }}}%}</p>
</div>
</ng-template>
</abp-extensible-table-row-detail>
```
### With Badges and Localization
```html
<abp-extensible-table-row-detail>
<ng-template let-row="row">
<div class="p-3 bg-light border rounded m-2">
<div class="row">
<div class="col-md-6">
<p class="mb-1"><strong>{%{{{ 'MyModule::Name' | abpLocalization }}}%}</strong></p>
<p class="text-muted">{%{{{ row.name }}}%}</p>
</div>
<div class="col-md-6">
<p class="mb-1"><strong>{%{{{ 'MyModule::Status' | abpLocalization }}}%}</strong></p>
<p>
@if (row.isActive) {
<span class="badge bg-success">{%{{{ 'AbpUi::Yes' | abpLocalization }}}%}</span>
} @else {
<span class="badge bg-secondary">{%{{{ 'AbpUi::No' | abpLocalization }}}%}</span>
}
</p>
</div>
</div>
</div>
</ng-template>
</abp-extensible-table-row-detail>
```
## Alternative: Direct Template Input
For simpler use cases, you can use the `rowDetailTemplate` input on `<abp-extensible-table>` directly:
```html
<abp-extensible-table
[data]="data.items"
[list]="list"
[rowDetailTemplate]="detailTemplate"
/>
<ng-template #detailTemplate let-row="row">
<div class="p-3">{%{{{ row.name }}}%}</div>
</ng-template>
```
## Events
### rowDetailToggle
The `rowDetailToggle` output emits when a row is expanded or collapsed:
```html
<abp-extensible-table
[data]="data.items"
[list]="list"
(rowDetailToggle)="onRowToggle($event)"
>
<abp-extensible-table-row-detail>
<ng-template let-row="row">...</ng-template>
</abp-extensible-table-row-detail>
</abp-extensible-table>
```
```typescript
onRowToggle(row: MyDto) {
console.log('Row toggled:', row);
}
```
## See Also
- [Data Table Column Extensions](data-table-column-extensions.md)
- [Entity Action Extensions](entity-action-extensions.md)
- [Extensions Overview](extensions-overall.md)

BIN
docs/en/framework/ui/angular/images/row-detail-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

10
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/extensible-table-row-detail.component.ts

@ -0,0 +1,10 @@
import { Component, contentChild, input, TemplateRef } from '@angular/core';
@Component({
selector: 'abp-extensible-table-row-detail',
template: '',
})
export class ExtensibleTableRowDetailComponent<R = any> {
readonly rowHeight = input<string | number>('100%');
readonly template = contentChild(TemplateRef<{ row: R; expanded: boolean }>);
}

1
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/index.ts

@ -0,0 +1 @@
export * from './extensible-table-row-detail.component';

169
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html

@ -1,21 +1,30 @@
@if (isBrowser) { @if (isBrowser) {
<ngx-datatable <ngx-datatable #table default [rows]="data" [count]="recordsTotal" [list]="list"
default [selectionType]="selectable ? _selectionType : undefined" (activate)="tableActivate.emit($event)"
[rows]="data" (select)="onSelect($event)" [selected]="selected" (scroll)="onScroll($event)" [scrollbarV]="infiniteScroll"
[count]="recordsTotal" [style.height]="getTableHeight()" [loadingIndicator]="infiniteScroll && isLoading"
[list]="list" [footerHeight]="infiniteScroll ? false : 50">
[selectionType]="selectable ? _selectionType : undefined" @if (effectiveRowDetailTemplate) {
(activate)="tableActivate.emit($event)" <ngx-datatable-row-detail [rowHeight]="effectiveRowDetailHeight">
(select)="onSelect($event)" <ng-template let-row="row" let-expanded="expanded" ngx-datatable-row-detail-template>
[selected]="selected" <ng-container
(scroll)="onScroll($event)" *ngTemplateOutlet="effectiveRowDetailTemplate; context: { row: row, expanded: expanded }" />
[scrollbarV]="infiniteScroll" </ng-template>
[style.height]="getTableHeight()" </ngx-datatable-row-detail>
[loadingIndicator]="infiniteScroll && isLoading"
[footerHeight]="infiniteScroll ? false : 50" <ngx-datatable-column [width]="50" [resizeable]="false" [sortable]="false" [draggable]="false"
> [canAutoResize]="false">
@if(selectable) { <ng-template let-row="row" let-expanded="expanded" ngx-datatable-cell-template>
<ngx-datatable-column [width]="50" [sortable]="false" [canAutoResize]="false" [draggable]="false" [resizeable]="false"> <button type="button" class="btn btn-link text-decoration-none text-muted p-0"
[attr.aria-label]="expanded ? 'Collapse' : 'Expand'" (click)="toggleExpandRow(row)">
<i class="fa" [class.fa-chevron-down]="!expanded" [class.fa-chevron-up]="expanded"></i>
</button>
</ng-template>
</ngx-datatable-column>
}
@if(selectable) {
<ngx-datatable-column [width]="50" [sortable]="false" [canAutoResize]="false" [draggable]="false"
[resizeable]="false">
<ng-template ngx-datatable-header-template let-value="value" let-allRowsSelected="allRowsSelected" <ng-template ngx-datatable-header-template let-value="value" let-allRowsSelected="allRowsSelected"
let-selectFn="selectFn"> let-selectFn="selectFn">
@ -44,97 +53,67 @@
</ngx-datatable-column> </ngx-datatable-column>
} }
@if (actionsTemplate || (actionList.length && hasAtLeastOnePermittedAction)) { @if (actionsTemplate || (actionList.length && hasAtLeastOnePermittedAction)) {
<ngx-datatable-column <ngx-datatable-column [name]="actionsText | abpLocalization" [maxWidth]="_actionsColumnWidth() ?? undefined"
[name]="actionsText | abpLocalization" [width]="_actionsColumnWidth() ?? 200" [canAutoResize]="!_actionsColumnWidth()" [sortable]="false">
[maxWidth]="_actionsColumnWidth() ?? undefined" <ng-template let-row="row" let-i="rowIndex" ngx-datatable-cell-template>
[width]="_actionsColumnWidth() ?? 200" <ng-container
[canAutoResize]="!_actionsColumnWidth()" *ngTemplateOutlet="actionsTemplate || gridActions; context: { $implicit: row, index: i }"></ng-container>
[sortable]="false" <ng-template #gridActions>
> @if (isVisibleActions(row)) {
<ng-template let-row="row" let-i="rowIndex" ngx-datatable-cell-template> <abp-grid-actions [index]="i" [record]="row" text="AbpUi::Actions"></abp-grid-actions>
<ng-container }
*ngTemplateOutlet="actionsTemplate || gridActions; context: { $implicit: row, index: i }"
></ng-container>
<ng-template #gridActions>
@if (isVisibleActions(row)) {
<abp-grid-actions [index]="i" [record]="row" text="AbpUi::Actions"></abp-grid-actions>
}
</ng-template>
</ng-template> </ng-template>
</ngx-datatable-column> </ng-template>
</ngx-datatable-column>
} }
@for (prop of propList; track prop.name; let i = $index) { @for (prop of propList; track prop.name; let i = $index) {
<ngx-datatable-column <ngx-datatable-column *abpVisible="prop.columnVisible(getInjected)" [width]="columnWidths[i] ?? 200"
*abpVisible="prop.columnVisible(getInjected)" [canAutoResize]="!columnWidths[i]"
[width]="columnWidths[i] ?? 200" [name]="(prop.isExtra ? '::' + prop.displayName : prop.displayName) | abpLocalization" [prop]="prop.name"
[canAutoResize]="!columnWidths[i]" [sortable]="prop.sortable">
[name]="(prop.isExtra ? '::' + prop.displayName : prop.displayName) | abpLocalization" <ng-template ngx-datatable-header-template let-column="column" let-sortFn="sortFn">
[prop]="prop.name" @if (prop.tooltip) {
[sortable]="prop.sortable" <span [ngbTooltip]="prop.tooltip.text | abpLocalization" [placement]="prop.tooltip.placement || 'auto'"
> container="body" [class.pointer]="prop.sortable" (click)="prop.sortable && sortFn(column)">
<ng-template ngx-datatable-header-template let-column="column" let-sortFn="sortFn"> {{ column.name }} <i class="fa fa-info-circle" aria-hidden="true"></i>
@if (prop.tooltip) { </span>
<span } @else {
[ngbTooltip]="prop.tooltip.text | abpLocalization" <span [class.pointer]="prop.sortable" (click)="prop.sortable && sortFn(column)">
[placement]="prop.tooltip.placement || 'auto'" {{ column.name }}
container="body" </span>
[class.pointer]="prop.sortable" }
(click)="prop.sortable && sortFn(column)" </ng-template>
> <ng-template let-row="row" let-i="index" ngx-datatable-cell-template>
{{ column.name }} <i class="fa fa-info-circle" aria-hidden="true"></i> <ng-container *abpPermission="prop.permission; runChangeDetection: false">
</span> <ng-container *abpVisible="row['_' + prop.name]?.visible">
} @else { @if (!row['_' + prop.name].component) {
<span @if (prop.type === 'datetime' || prop.type === 'date' || prop.type === 'time') {
[class.pointer]="prop.sortable" <div [innerHTML]="
(click)="prop.sortable && sortFn(column)"
>
{{ column.name }}
</span>
}
</ng-template>
<ng-template let-row="row" let-i="index" ngx-datatable-cell-template>
<ng-container *abpPermission="prop.permission; runChangeDetection: false">
<ng-container *abpVisible="row['_' + prop.name]?.visible">
@if (!row['_' + prop.name].component) {
@if (prop.type === 'datetime' || prop.type === 'date' || prop.type === 'time') {
<div
[innerHTML]="
!prop.isExtra !prop.isExtra
? (row['_' + prop.name]?.value | async | abpUtcToLocal:prop.type) ? (row['_' + prop.name]?.value | async | abpUtcToLocal:prop.type)
: ('::' + (row['_' + prop.name]?.value | async | abpUtcToLocal:prop.type) | abpLocalization) : ('::' + (row['_' + prop.name]?.value | async | abpUtcToLocal:prop.type) | abpLocalization)
" " (click)="
(click)="
prop.action && prop.action({ getInjected: getInjected, record: row, index: i }) prop.action && prop.action({ getInjected: getInjected, record: row, index: i })
" " [ngClass]="entityPropTypeClasses[prop.type]" [class.pointer]="prop.action"></div>
[ngClass]="entityPropTypeClasses[prop.type]" } @else {
[class.pointer]="prop.action" <div [innerHTML]="
></div>
} @else {
<div
[innerHTML]="
!prop.isExtra !prop.isExtra
? (row['_' + prop.name]?.value | async) ? (row['_' + prop.name]?.value | async)
: ('::' + (row['_' + prop.name]?.value | async) | abpLocalization) : ('::' + (row['_' + prop.name]?.value | async) | abpLocalization)
" " (click)="
(click)="
prop.action && prop.action({ getInjected: getInjected, record: row, index: i }) prop.action && prop.action({ getInjected: getInjected, record: row, index: i })
" " [ngClass]="entityPropTypeClasses[prop.type]" [class.pointer]="prop.action"></div>
[ngClass]="entityPropTypeClasses[prop.type]" }
[class.pointer]="prop.action" } @else {
></div> <ng-container *ngComponentOutlet="
}
} @else {
<ng-container
*ngComponentOutlet="
row['_' + prop.name].component; row['_' + prop.name].component;
injector: row['_' + prop.name].injector injector: row['_' + prop.name].injector
" "></ng-container>
></ng-container> }
}
</ng-container>
</ng-container> </ng-container>
</ng-template> </ng-container>
</ngx-datatable-column> </ng-template>
</ngx-datatable-column>
} }
</ngx-datatable> </ngx-datatable>
} }

36
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts

@ -4,6 +4,7 @@ import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
computed, computed,
ContentChild,
EventEmitter, EventEmitter,
inject, inject,
Injector, Injector,
@ -17,13 +18,14 @@ import {
SimpleChanges, SimpleChanges,
TemplateRef, TemplateRef,
TrackByFunction, TrackByFunction,
ViewChild,
} from '@angular/core'; } from '@angular/core';
import { AsyncPipe, isPlatformBrowser, NgComponentOutlet, NgTemplateOutlet } from '@angular/common'; import { AsyncPipe, isPlatformBrowser, NgComponentOutlet, NgTemplateOutlet } from '@angular/common';
import { Observable, filter, map, Subject, debounceTime, distinctUntilChanged } from 'rxjs'; import { Observable, filter, map, Subject, debounceTime, distinctUntilChanged } from 'rxjs';
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
import { NgxDatatableModule, SelectionType } from '@swimlane/ngx-datatable'; import { NgxDatatableModule, SelectionType, DatatableComponent } from '@swimlane/ngx-datatable';
import { import {
ABP, ABP,
@ -53,6 +55,8 @@ import {
ROW_RECORD, ROW_RECORD,
} from '../../tokens/extensions.token'; } from '../../tokens/extensions.token';
import { GridActionsComponent } from '../grid-actions/grid-actions.component'; import { GridActionsComponent } from '../grid-actions/grid-actions.component';
import { ExtensibleTableRowDetailComponent } from './extensible-table-row-detail';
import { RowDetailContext } from '../../models/row-detail';
const DEFAULT_ACTIONS_COLUMN_WIDTH = 150; const DEFAULT_ACTIONS_COLUMN_WIDTH = 150;
@ -75,6 +79,12 @@ const DEFAULT_ACTIONS_COLUMN_WIDTH = 150;
], ],
templateUrl: './extensible-table.component.html', templateUrl: './extensible-table.component.html',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
styles: [`
:host ::ng-deep .ngx-datatable.material .datatable-body .datatable-row-detail {
background: none;
padding: 0;
}
`],
}) })
export class ExtensibleTableComponent<R = any> implements OnChanges, AfterViewInit, OnDestroy { export class ExtensibleTableComponent<R = any> implements OnChanges, AfterViewInit, OnDestroy {
readonly #injector = inject(Injector); readonly #injector = inject(Injector);
@ -127,6 +137,23 @@ export class ExtensibleTableComponent<R = any> implements OnChanges, AfterViewIn
@Output() loadMore = new EventEmitter<void>(); @Output() loadMore = new EventEmitter<void>();
@Input() tableHeight: number; @Input() tableHeight: number;
@Input() rowDetailTemplate?: TemplateRef<RowDetailContext<R>>;
@Input() rowDetailHeight: string | number = '100%';
@Output() rowDetailToggle = new EventEmitter<R>();
@ContentChild(ExtensibleTableRowDetailComponent)
rowDetailComponent?: ExtensibleTableRowDetailComponent<R>;
@ViewChild('table', { static: false }) table!: DatatableComponent;
protected get effectiveRowDetailTemplate(): TemplateRef<RowDetailContext<R>> | undefined {
return this.rowDetailComponent?.template() ?? this.rowDetailTemplate;
}
protected get effectiveRowDetailHeight(): string | number {
return this.rowDetailComponent?.rowHeight() ?? this.rowDetailHeight;
}
hasAtLeastOnePermittedAction: boolean; hasAtLeastOnePermittedAction: boolean;
readonly propList: EntityPropList<R>; readonly propList: EntityPropList<R>;
@ -290,6 +317,13 @@ export class ExtensibleTableComponent<R = any> implements OnChanges, AfterViewIn
return this.tableHeight ? `${this.tableHeight}px` : 'auto'; return this.tableHeight ? `${this.tableHeight}px` : 'auto';
} }
toggleExpandRow(row: R): void {
if (this.table && this.table.rowDetail) {
this.table.rowDetail.toggleExpandRow(row);
}
this.rowDetailToggle.emit(row);
}
ngAfterViewInit(): void { ngAfterViewInit(): void {
if (!this.infiniteScroll) { if (!this.infiniteScroll) {
this.list?.requestStatus$?.pipe(filter(status => status === 'loading')).subscribe(() => { this.list?.requestStatus$?.pipe(filter(status => status === 'loading')).subscribe(() => {

1
npm/ng-packs/packages/components/extensible/src/lib/components/index.ts

@ -2,6 +2,7 @@ export * from './abstract-actions/abstract-actions.component';
export * from './extensible-form/extensible-form.component'; export * from './extensible-form/extensible-form.component';
export * from './extensible-form/extensible-form-prop.component'; export * from './extensible-form/extensible-form-prop.component';
export * from './extensible-table/extensible-table.component'; export * from './extensible-table/extensible-table.component';
export * from './extensible-table/extensible-table-row-detail';
export * from './date-time-picker/extensible-date-time-picker.component'; export * from './date-time-picker/extensible-date-time-picker.component';
export * from './grid-actions/grid-actions.component'; export * from './grid-actions/grid-actions.component';
export * from './page-toolbar/page-toolbar.component'; export * from './page-toolbar/page-toolbar.component';

4
npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts

@ -13,6 +13,7 @@ import {
ExtensibleFormComponent, ExtensibleFormComponent,
ExtensibleFormPropComponent, ExtensibleFormPropComponent,
ExtensibleTableComponent, ExtensibleTableComponent,
ExtensibleTableRowDetailComponent,
GridActionsComponent, GridActionsComponent,
PageToolbarComponent, PageToolbarComponent,
ExtensibleDateTimePickerComponent, ExtensibleDateTimePickerComponent,
@ -32,6 +33,7 @@ const importWithExport = [
CreateInjectorPipe, CreateInjectorPipe,
ExtensibleFormComponent, ExtensibleFormComponent,
ExtensibleTableComponent, ExtensibleTableComponent,
ExtensibleTableRowDetailComponent,
ExtensibleFormMultiselectComponent, ExtensibleFormMultiselectComponent,
]; ];
@ -50,4 +52,4 @@ const importWithExport = [
], ],
exports: [...importWithExport], exports: [...importWithExport],
}) })
export class ExtensibleModule {} export class ExtensibleModule { }

4
npm/ng-packs/packages/components/extensible/src/lib/models/row-detail.ts

@ -0,0 +1,4 @@
export interface RowDetailContext<R = any> {
row: R;
expanded: boolean;
}

2
npm/ng-packs/packages/components/extensible/src/public-api.ts

@ -2,6 +2,7 @@ export * from './lib/components/date-time-picker/extensible-date-time-picker.com
export * from './lib/components/extensible-form/extensible-form-prop.component'; export * from './lib/components/extensible-form/extensible-form-prop.component';
export * from './lib/components/extensible-form/extensible-form.component'; export * from './lib/components/extensible-form/extensible-form.component';
export * from './lib/components/extensible-table/extensible-table.component'; export * from './lib/components/extensible-table/extensible-table.component';
export * from './lib/components/extensible-table/extensible-table-row-detail';
export * from './lib/components/grid-actions/grid-actions.component'; export * from './lib/components/grid-actions/grid-actions.component';
export * from './lib/components/page-toolbar/page-toolbar.component'; export * from './lib/components/page-toolbar/page-toolbar.component';
export * from './lib/components/multi-select'; export * from './lib/components/multi-select';
@ -68,4 +69,5 @@ export * from './lib/utils/form-props.util';
export * from './lib/utils/props.util'; export * from './lib/utils/props.util';
export * from './lib/utils/state.util'; export * from './lib/utils/state.util';
export * from './lib/utils/model.utils'; export * from './lib/utils/model.utils';
export * from './lib/models/row-detail';
export * from './lib/extensible.module'; export * from './lib/extensible.module';

Loading…
Cancel
Save