[ADF-4122] Add sticky header feature to datatable and refactor styles (#4370)

* [ADF-4122] Add sticky header config to datatable and refactor styles

* [ADF-4122] Fix core unit tests

* [ADF-4122] Commit requested changes

* [ADF-4122] Fix e2e tests

* Update docs/core/datatable.component.md

Co-Authored-By: davidcanonieto <david.cano@alfresco.com>
This commit is contained in:
davidcanonieto
2019-03-01 17:23:31 +01:00
committed by Eugenio Romano
parent a7d058fd2e
commit 7da9bd89cb
27 changed files with 418 additions and 323 deletions

View File

@@ -10,12 +10,12 @@ router-outlet[name='overlay'] + * {
@media (max-width: 425px) {
adf-content-node-selector {
.adf-content-node-selector-content-list {
.adf-data-table-cell {
.adf-datatable-cell {
display: none;
}
.adf-data-table-cell:first-child,
.adf-data-table-cell:nth-child(2) {
.adf-datatable-cell:first-child,
.adf-datatable-cell:nth-child(2) {
display: table-cell;
}
}

View File

@@ -2,17 +2,25 @@
<input id="adf-datatable-filter-input" matInput placeholder="Name filter" [(ngModel)]="data.filterValue">
</mat-form-field>
<mat-slide-toggle
id="adf-toggle-sticky-header"
[color]="'primary'"
(change)="toggleStickyHeader()"
[checked]="stickyHeader">
Sticky header
</mat-slide-toggle>
<div style="height: 300px; overflow-y: auto;">
<adf-datatable
#dataTable
[data]="data"
[stickyHeader]="stickyHeader"
[selectionMode]="selectionMode"
[multiselect]="multiselect"
[actions]="true"
rowStyleClass="custom-row-style"
(showRowActionsMenu)="onShowRowActionsMenu($event)"
(executeRowAction)="onExecuteRowAction($event)"
(row-click)="onRowClick($event)"
(row-dblclick)="onRowDblClick($event)">
(showRowActionsMenu)="onShowRowActionsMenu($event)" (executeRowAction)="onExecuteRowAction($event)"
(row-click)="onRowClick($event)" (row-dblclick)="onRowDblClick($event)">
<!-- HTML column definition demo -->
<!--
<data-columns>
@@ -24,6 +32,7 @@
</data-columns>
-->
</adf-datatable>
</div>
<div>
Selected items: {{ dataTable.selection?.length }}

View File

@@ -54,6 +54,7 @@ export class DataTableComponent {
multiselect = false;
data: FilteredDataAdapter;
stickyHeader = false;
@Input()
selectionMode = 'single';
@@ -100,18 +101,18 @@ export class DataTableComponent {
},
{
id: 4,
name: 'Image 1',
name: 'Image 8',
createdOn: new Date(2016, 6, 2, 15, 8, 4),
createdBy: this._createdBy,
icon: this._imageUrl
icon: 'material-icons://alarm'
}
],
[
{ type: 'image', key: 'icon', title: '', srTitle: 'Thumbnail' },
{ type: 'text', key: 'id', title: 'Id', sortable: true , cssClass: 'adf-desktop-only'},
{ type: 'text', key: 'createdOn', title: 'Created On', sortable: true },
{ type: 'text', key: 'name', title: 'Name', cssClass: 'adf-full-width adf-name-column', sortable: true },
{ type: 'text', key: 'createdBy.name', title: 'Created By', sortable: true, cssClass: 'adf-desktop-only'}
{ type: 'text', key: 'id', title: 'Id', sortable: true , cssClass: 'adf-ellipsis-cell' },
{ type: 'text', key: 'createdOn', title: 'Created On', sortable: true, cssClass: 'adf-ellipsis-cell adf-expand-cell-5' },
{ type: 'text', key: 'name', title: 'Name', cssClass: '', sortable: true },
{ type: 'text', key: 'createdBy.name', title: 'Created By', sortable: true, cssClass: ''}
]
);
@@ -199,4 +200,8 @@ export class DataTableComponent {
onRowDblClick(event) {
this.logService.log(event);
}
toggleStickyHeader() {
this.stickyHeader = !this.stickyHeader;
}
}

View File

@@ -202,6 +202,7 @@
<adf-document-list
#documentList
class="adf-file-list-container"
[permissionsStyle]="permissionsStyle"
[currentFolderId]="currentFolderId"
[contextMenuActions]="true"
@@ -244,7 +245,7 @@
key="name"
title="{{'DOCUMENT_LIST.COLUMNS.DISPLAY_NAME' | translate}}"
[formatTooltip]="getNodeNameTooltip"
class="adf-full-width adf-ellipsis-cell">
class="adf-ellipsis-cell adf-expand-cell-5">
<ng-template let-context>
<adf-name-column [context]="context"></adf-name-column>
</ng-template>
@@ -254,7 +255,7 @@
key="name"
title="{{'DOCUMENT_LIST.COLUMNS.DISPLAY_NAME' | translate}}"
[formatTooltip]="getNodeNameTooltip"
class="adf-full-width adf-ellipsis-cell">
class="adf-ellipsis-cell adf-expand-cell-5">
</data-column>
<!-- Location column demo -->
<!--

View File

@@ -38,9 +38,13 @@
adf-document-list ::ng-deep .adf-datatable-selected > svg {
fill: #00bcd4 !important;
margin-top: -4px;
margin-left: -4px;
width: 32px;
height: 32px;
}
.adf-data-table-card .adf-lock-button {
.adf-datatable-card .adf-lock-button {
top: -10px;
}
@@ -129,15 +133,15 @@
}
@media (max-device-width: $minimumDocumentListWidth) {
adf-document-list .adf-data-table {
.adf-data-table-cell,
.adf-file-list-container .adf-datatable-list {
.adf-datatable-cell,
.adf-datatable-table-cell-header {
display: none;
}
.adf-data-table-cell:first-child,
.adf-datatable-cell:first-child,
.adf-datatable-table-cell-header:first-child,
.adf-data-table-cell:nth-child(2),
.adf-datatable-cell:nth-child(2),
.adf-datatable-table-cell-header:nth-child(2) {
display: table-cell;
}
@@ -163,9 +167,9 @@
}
@media (max-device-width: 1024px) {
adf-document-list .adf-data-table {
.adf-file-list-container .adf-datatable-list {
.adf-data-table-cell:nth-child(5),
.adf-datatable-cell:nth-child(5),
.adf-datatable-table-cell-header:nth-child(5) {
display: none;
}

View File

@@ -71,23 +71,23 @@
}
.adf-list {
.adf-data-table {
.adf-datatable-list {
border: none;
}
.adf-data-table tr,
.adf-data-table td {
.adf-datatable tr,
.adf-datatable td {
height: 36px;
font-size: 14px;
}
.adf-data-table td {
.adf-datatable td {
padding-top: 0;
padding-bottom: 0;
border-bottom: none;
}
.adf-data-table th {
.adf-datatable th {
padding-top: 0;
padding-bottom: 0;
height: 40px;

View File

@@ -57,7 +57,7 @@
</data-column>
<data-column
class="adf-data-table-cell--ellipsis__name"
class="adf-datatable-cell--ellipsis__name"
key="name"
title="DOCUMENT_LIST.COLUMNS.DISPLAY_NAME">
<ng-template let-value="value" let-context>
@@ -80,7 +80,7 @@
</data-column>
<data-column
class="adf-data-table-cell--ellipsis"
class="adf-datatable-cell--ellipsis"
key="archivedByUser.displayName"
title="DOCUMENT_LIST.COLUMNS.DELETED_BY">
</data-column>

View File

@@ -231,7 +231,7 @@ Let's start by assigning an "image-table-cell" class to the thumbnail column:
Now your application can define styles to change the content of the column based on conditions such as the selection state:
```css
adf-document-list ::ng-deep adf-datatable > table > tbody > tr.is-selected > td.adf-data-table-cell.adf-data-table-cell--image.adf-image-table-cell > div > div > mat-icon > svg {
adf-document-list ::ng-deep adf-datatable > table > tbody > tr.is-selected > td.adf-datatable-cell.adf-datatable-cell--image.adf-image-table-cell > div > div > mat-icon > svg {
fill: #00bcd4;
}
```

View File

@@ -27,6 +27,7 @@ See it live: [DataTable Quickstart](https://embed.plnkr.co/80qr4YFBeHjLMdAV0F6l/
- [DataTable DOM Events](#datatable-dom-events)
- [Card view](#card-view)
- [Using events](#using-events)
- [Customizing the component's styles](#customizing-the-components-styles)
- [See also](#see-also)
## Basic usage
@@ -326,6 +327,7 @@ together in the same datatable.
| selectionMode | `string` | "single" | Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode, you can use Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows. |
| showHeader | `boolean` | true | Toggles the header. |
| sorting | `any[]` | \[] | Define the sort order of the datatable. Possible values are : [`created`, `desc`], [`created`, `asc`], [`due`, `desc`], [`due`, `asc`] |
| stickyHeader | `boolean` | false | Toggles a sticky (fixed) header that stays in place while the rows become scrollable. [Sticky header](#sticky-header)|
### Events
@@ -561,6 +563,80 @@ When an action is selected in the dropdown menu, the DataTable invokes the `exec
Use this to handle the response, inspect the action payload (and all custom properties defined
earlier), and perform the corresponding actions.
### Customizing the component's styles
The datatable component is ready to be used out of the box although you might want to customize it to better fit your needs.
### Truncated text
By default, the content of the cells is wrapped so you can see all the data inside. See picture bellow.
![](../docassets/images/datatable-wrapped-text.png)
However, you can also truncate the text within these cells by using the `adf-ellipsis-cell` class in the desired column.
```json
{
type: 'text',
key: 'createdOn',
title: 'Created On',
sortable: true,
cssClass: 'adf-ellipsis-cell'
}
```
![](../docassets/images/datatable-truncated-text.png)
### Expanded cells
This component makes use of a flex layout. All cells have the same semantic importance, so all of them have the same width. You can alter this behavior by adding one of the following classes in the desired column to make it grow wider.
![](../docassets/images/datatable-expand-5.png)
This classes go from `adf-expand-cell-1` to `adf-expand-cell-5`. The higher the number in the class the wider the column will get. You can choose the one that better suits your needs.
### Combining classes
All these classes can be combined together to fully fit your needs and make your datatable component look as you want.
```json
{
type: 'text',
key: 'name',
title: 'Name',
cssClass: 'adf-ellipsis-cell adf-expand-cell-3'
}
```
### Sticky header
If you have a long list of rows and you want to fix the header in place so you always see it you only have to follow this 2-step process.
First, you will need to set the stickyHeader property of your datatable to `true`:
```html
<adf-datatable
[data]="data"
[stickyHeader]="true">
</adf-datatable>
```
Second and last, you will need to set the height of your datatable in its parent's container so that the datatable adapts to it, i.e. `height: 300px;`. You will also need to set the overflow-y attribute to auto, `overflow-y: auto;`, so it hides when the rows exceed the height of the datatable component.
```html
<div style="height: 300px; overflow-y: auto;">
<adf-datatable
[data]="data"
[stickyHeader]="'true'">
</adf-datatable>
</div>
```
Final result
![](../docassets/images/datatable-sticky-header.png)
## See also
- [Data column component](data-column.component.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -62,7 +62,7 @@ export class ContentServicesPage {
emptyFolderImage = element(by.css('.adf-empty-folder-image'));
emptyRecent = element(by.css('.adf-container-recent .adf-empty-list__title'));
gridViewButton = element(by.css('button[data-automation-id="document-list-grid-view"]'));
cardViewContainer = element(by.css('div.adf-document-list-container div.adf-data-table-card'));
cardViewContainer = element(by.css('div.adf-document-list-container div.adf-datatable-card'));
copyButton = element(by.css('button[data-automation-id="content-node-selector-actions-choose"]'));
searchInputElement = element(by.css('input[data-automation-id="content-node-selector-search-input"]'));
shareNodeButton = element(by.cssContainingText('mat-icon', ' share '));
@@ -559,7 +559,7 @@ export class ContentServicesPage {
}
checkLockIsDisplayedForElement(name) {
let lockButton = element(by.css(`div.adf-data-table-cell[filename="${name}"] button`));
let lockButton = element(by.css(`div.adf-datatable-cell[filename="${name}"] button`));
Util.waitUntilElementIsVisible(lockButton);
}
@@ -572,7 +572,7 @@ export class ContentServicesPage {
}
async getStyleValueForRowText(rowName, styleName) {
let row = element(by.css(`div.adf-data-table-cell[filename="${rowName}"] span.adf-datatable-cell-value[title="${rowName}"]`));
let row = element(by.css(`div.adf-datatable-cell[filename="${rowName}"] span.adf-datatable-cell-value[title="${rowName}"]`));
Util.waitUntilElementIsVisible(row);
return row.getCssValue(styleName);
}
@@ -596,7 +596,7 @@ export class ContentServicesPage {
}
checkIconForRowIsDisplayed(fileName) {
let iconRow = element(by.css(`.adf-document-list-container div.adf-data-table-cell[filename="${fileName}"] img`));
let iconRow = element(by.css(`.adf-document-list-container div.adf-datatable-cell[filename="${fileName}"] img`));
Util.waitUntilElementIsVisible(iconRow);
return iconRow;
}
@@ -621,22 +621,22 @@ export class ContentServicesPage {
getCardElementShowedInPage() {
this.checkCardViewContainerIsDisplayed();
let actualCards = $$('div.adf-document-list-container div.adf-data-table-card div.adf-cell-value img').count();
let actualCards = $$('div.adf-document-list-container div.adf-datatable-card div.adf-cell-value img').count();
return actualCards;
}
getDocumentCardIconForElement(elementName) {
let elementIcon = element(by.css(`.adf-document-list-container div.adf-data-table-cell[filename="${elementName}"] img`));
let elementIcon = element(by.css(`.adf-document-list-container div.adf-datatable-cell[filename="${elementName}"] img`));
return elementIcon.getAttribute('src');
}
checkDocumentCardPropertyIsShowed(elementName, propertyName) {
let elementProperty = element(by.css(`.adf-document-list-container div.adf-data-table-cell[filename="${elementName}"][title="${propertyName}"]`));
let elementProperty = element(by.css(`.adf-document-list-container div.adf-datatable-cell[filename="${elementName}"][title="${propertyName}"]`));
Util.waitUntilElementIsVisible(elementProperty);
}
getAttributeValueForElement(elementName, propertyName) {
let elementSize = element(by.css(`.adf-document-list-container div.adf-data-table-cell[filename="${elementName}"][title="${propertyName}"] span`));
let elementSize = element(by.css(`.adf-document-list-container div.adf-datatable-cell[filename="${elementName}"][title="${propertyName}"] span`));
return elementSize.getText();
}
@@ -646,9 +646,9 @@ export class ContentServicesPage {
}
navigateToCardFolder(folderName) {
let folderCard = element(by.css(`.adf-document-list-container div.adf-image-table-cell.adf-data-table-cell[filename="${folderName}"]`));
let folderCard = element(by.css(`.adf-document-list-container div.adf-image-table-cell.adf-datatable-cell[filename="${folderName}"]`));
folderCard.click();
let folderSelected = element(by.css(`.adf-datatable-row.adf-is-selected div[filename="${folderName}"].adf-data-table-cell--image`));
let folderSelected = element(by.css(`.adf-datatable-row.adf-is-selected div[filename="${folderName}"].adf-datatable-cell--image`));
Util.waitUntilElementIsVisible(folderSelected);
browser.actions().sendKeys(protractor.Key.ENTER).perform();
}

View File

@@ -33,7 +33,7 @@ export class ContentListPage {
actionMenu = element(by.css('div[role="menu"]'));
optionButton = by.css('button[data-automation-id*="action_menu_"]');
rowByRowName = by.xpath('ancestor::div[contains(@class, "adf-datatable-row")]');
nameColumn = by.css('div[class*="datatable-body"] div[class*="adf-data-table-cell"][title="Display name"]');
nameColumn = by.css('div[class*="datatable-body"] div[class*="adf-datatable-cell"][title="Display name"]');
nameColumnHeader = by.css('div[data-automation-id="auto_id_name"]');
createdByColumn = by.css('div[class*="--text"][title="Created by"] span');
sizeColumn = by.css('div[id*="document-list-container"] div[class*="adf-datatable-row"] .adf-filesize-cell');

View File

@@ -33,7 +33,7 @@ export class TasksPage {
rowByRowName = by.xpath('ancestor::mat-chip');
checklistContainer = by.css('div[class*="checklist-menu"]');
taskTitle = 'h2[class="adf-activiti-task-details__header"] span';
rows = by.css('div[class*="adf-datatable-body"] div[class*="adf-datatable-row"] div[class*="adf-data-table-cell"]');
rows = by.css('div[class*="adf-datatable-body"] div[class*="adf-datatable-row"] div[class*="adf-datatable-cell"]');
completeButtonNoForm = element(by.id('adf-no-form-complete-button'));
checklistDialog = element(by.id('checklist-dialog'));
checklistNoMessage = element(by.id('checklist-none-message'));

View File

@@ -84,7 +84,7 @@
</data-column>
<data-column key="modifiedAt" type="date" format="timeAgo" class="adf-content-selector-modified-cell"></data-column>
<data-column key="createdByUser.displayName" type="text" class="adf-content-selector-modifier-cell"></data-column>
<data-column key="visibility" type="text"></data-column>
<data-column key="visibility" type="text" class="adf-content-selector-visibility-cell"></data-column>
</data-columns>
</adf-document-list>

View File

@@ -84,15 +84,20 @@
height: 200px;
overflow: auto;
border: 1px solid mat-color($foreground, base, 0.07);
padding: 1px;
border-top: 0;
.adf-highlight {
color: mat-color($primary);
}
.adf-data-table {
.adf-datatable-list {
border: none;
.adf-datatable-selected {
height: 100%;
width: 100%;
}
.adf-datatable-selected > svg {
fill: #00bcd4 !important;
}
@@ -102,29 +107,14 @@
border: none !important;
}
.adf-data-table-cell {
padding-top: 8px;
padding-bottom: 8px;
height: 30px;
.adf-datatable-cell {
& .adf-name-location-cell-location {
display: none;
}
& .adf-name-location-cell-name {
padding: 0;
}
&--image {
padding: 8px 16px;
}
&--text {
padding: 8px 16px;
}
&:first-of-type {
padding: 8px 16px;
&.adf-content-selector-visibility-cell {
display: none;
}
}
@@ -132,13 +122,13 @@
height: auto !important;
&:first-child {
.adf-data-table-cell {
.adf-datatable-cell {
border-top: none;
}
}
&:last-child {
.adf-data-table-cell {
.adf-datatable-cell {
border-bottom: none;
}
}
@@ -146,11 +136,10 @@
}
&-searchLayout {
height: auto !important;
.adf-data-table {
.adf-data-table-cell {
height: 56px;
padding-bottom: 8px;
.adf-datatable-list {
.adf-datatable-cell {
& .adf-name-location-cell-location {
display: block;
@@ -168,10 +157,8 @@
display: none;
}
&--image {
padding-left: 16px;
padding-right: 8px;
padding-bottom: 8px;
&.adf-content-selector-visibility-cell {
display: none;
}
}
}

View File

@@ -2,10 +2,15 @@
$foreground: map-get($theme, foreground);
$accent: map-get($theme, accent);
.mat-icon.adf-datatable-selected {
height: 100%;
width: 100%;
margin-left: -2px;
margin-top: 2px;
}
.adf-datatable-selected > svg {
fill: mat-color($accent);
margin-top: -4px;
margin-left: -4px;
width: 32px;
height: 32px;
}
@@ -154,7 +159,7 @@
}
.adf-datatable-gallery-thumbnails {
.adf-data-table-card .adf-datatable-row {
.adf-datatable-card .adf-datatable-row {
height: 300px !important;
img {
@@ -168,7 +173,7 @@
justify-content: center;
}
.adf-data-table-cell.adf-datatable-table-cell.adf-data-table-cell--image {
.adf-datatable-cell.adf-datatable-table-cell.adf-datatable-cell--image {
flex: 0 0 auto;
display: flex;
flex-direction: column-reverse;

View File

@@ -218,7 +218,7 @@ describe('DocumentList', () => {
fixture.detectChanges();
expect(element.querySelector('.adf-data-table-card')).toBeDefined();
expect(element.querySelector('.adf-datatable-card')).toBeDefined();
});
it('should use the base document list style if cardview is false', () => {
@@ -226,8 +226,8 @@ describe('DocumentList', () => {
fixture.detectChanges();
expect(element.querySelector('.adf-data-table-card')).toBe(null);
expect(element.querySelector('.adf-data-table')).toBeDefined();
expect(element.querySelector('.adf-datatable-card')).toBe(null);
expect(element.querySelector('.adf-datatable')).toBeDefined();
});
it('should reset selection upon reload', () => {

View File

@@ -1,25 +1,26 @@
<div
role="grid"
*ngIf="data" class="adf-full-width"
[class.adf-data-table-card]="display === 'gallery'"
[class.adf-data-table]="display === 'list'"
[class.adf-data-table--empty]="!isHeaderVisible()">
[class.adf-datatable-card]="display === 'gallery'"
[class.adf-datatable-list]="display === 'list'"
[class.adf-sticky-header]="stickyHeader"
[class.adf-datatable--empty]="!isHeaderVisible()">
<div *ngIf="showHeader && isHeaderVisible()" class="adf-datatable-header" role="rowgroup">
<div class="adf-datatable-row" *ngIf="display === 'list'" role="row">
<!-- Actions (left) -->
<div *ngIf="actions && actionsPosition === 'left'" class="adf-actions-column adf-datatable-table-cell-header">
<div *ngIf="actions && actionsPosition === 'left'" class="adf-actions-column adf-datatable-cell-header">
<span class="adf-sr-only">Actions</span>
</div>
<!-- Columns -->
<div *ngIf="multiselect" class="adf-datatable-table-cell-header">
<div *ngIf="multiselect" class="adf-datatable-cell-header adf-datatable-checkbox">
<mat-checkbox [checked]="isSelectAllChecked" (change)="onSelectAllClick($event)"></mat-checkbox>
</div>
<div class="adf-data-table-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-table-cell-header"
<div class="adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-cell-header"
*ngFor="let col of data.getColumns()"
[class.adf-sortable]="col.sortable"
[attr.data-automation-id]="'auto_id_' + col.key"
[class.adf-data-table__header--sorted-asc]="isColumnSorted(col, 'asc')"
[class.adf-data-table__header--sorted-desc]="isColumnSorted(col, 'desc')"
[class.adf-datatable__header--sorted-asc]="isColumnSorted(col, 'asc')"
[class.adf-datatable__header--sorted-desc]="isColumnSorted(col, 'desc')"
(click)="onColumnHeaderClick(col)"
(keyup.enter)="onColumnHeaderClick(col)"
role="columnheader"
@@ -29,7 +30,7 @@
<span *ngIf="col.title">{{ col.title | translate}}</span>
</div>
<!-- Actions (right) -->
<div *ngIf="actions && actionsPosition === 'right'" class="adf-actions-column adf-datatable-table-cell-header">
<div *ngIf="actions && actionsPosition === 'right'" class="adf-actions-column adf-datatable-cell-header adf-datatable__actions-cell">
<span class="adf-sr-only">Actions</span>
</div>
</div>
@@ -57,7 +58,7 @@
[ngClass]="getRowStyle(row)"
(keyup)="onRowKeyUp(row, $event)">
<!-- Actions (left) -->
<div *ngIf="actions && actionsPosition === 'left'" role="gridcell" class="adf-datatable-table-cell">
<div *ngIf="actions && actionsPosition === 'left'" role="gridcell" class="adf-datatable-cell">
<button mat-icon-button [matMenuTriggerFor]="menu"
[title]="'ADF-DATATABLE.CONTENT-ACTIONS.TOOLTIP' | translate"
[attr.id]="'action_menu_left_' + idx"
@@ -75,7 +76,7 @@
</mat-menu>
</div>
<div *ngIf="multiselect" class="adf-datatable-table-cell adf-datatable-table-checkbox">
<div *ngIf="multiselect" class="adf-datatable-cell adf-datatable-checkbox">
<mat-checkbox
[checked]="row.isSelected"
[attr.aria-checked]="row.isSelected"
@@ -85,7 +86,7 @@
</div>
<div *ngFor="let col of data.getColumns()"
role="gridcell"
class="adf-data-table-cell adf-datatable-table-cell adf-data-table-cell--{{col.type || 'text'}} {{col.cssClass}}"
class=" adf-datatable-cell adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}}"
[attr.title]="col.title | translate"
[attr.filename]="getFilename(row)"
tabindex="0"
@@ -93,7 +94,7 @@
(keydown.enter)="onEnterKeyPressed(row, $event)"
[adf-context-menu]="getContextMenuActions(row, col)"
[adf-context-menu-enabled]="contextMenu">
<div *ngIf="!col.template" class="adf-cell-container">
<div *ngIf="!col.template" class="adf-datatable-cell-container">
<ng-container [ngSwitch]="col.type">
<div *ngSwitchCase="'image'" class="adf-cell-value">
<mat-icon *ngIf="isIconValue(row, col); else no_iconvalue">{{ asIconValue(row, col) }}
@@ -156,7 +157,7 @@
</span>
</ng-container>
</div>
<div *ngIf="col.template" class="adf-cell-container">
<div *ngIf="col.template" class="adf-datatable-cell-container">
<ng-container
[ngTemplateOutlet]="col.template"
[ngTemplateOutletContext]="{ $implicit: { data: data, row: row, col: col }, value: data.getValue(row, col) }">
@@ -167,7 +168,7 @@
<!-- Actions (right) -->
<div *ngIf="actions && actionsPosition === 'right'"
role="gridcell"
class="adf-datatable-table-cell adf-datatable__actions-cell">
class="adf-datatable-cell adf-datatable__actions-cell">
<button mat-icon-button [matMenuTriggerFor]="menu"
[title]="'ADF-DATATABLE.CONTENT-ACTIONS.TOOLTIP' | translate"
[attr.id]="'action_menu_right_' + idx"
@@ -190,8 +191,8 @@
<div *ngIf="isEmpty()"
role="row"
[class.adf-datatable-row]="display === 'list'"
[class.adf-data-table-card-empty]="display === 'gallery'">
<div class="adf-no-content-container adf-datatable-table-cell" role="gridcell">
[class.adf-datatable-card-empty]="display === 'gallery'">
<div class="adf-no-content-container adf-datatable-cell" role="gridcell">
<ng-template *ngIf="noContentTemplate"
ngFor [ngForOf]="[data]"
[ngForTemplate]="noContentTemplate">
@@ -206,9 +207,9 @@
<div *ngIf="!loading && noPermission"
role="row"
[class.adf-datatable-row]="display === 'list'"
[class.adf-data-table-card-permissions]="display === 'gallery'"
[class.adf-datatable-card-permissions]="display === 'gallery'"
class="adf-no-permission__row">
<div class="adf-no-permission__cell adf-no-content-container adf-datatable-table-cell">
<div class="adf-no-permission__cell adf-no-content-container adf-datatable-cell">
<ng-template *ngIf="noPermissionTemplate"
ngFor [ngForOf]="[data]"
[ngForTemplate]="noPermissionTemplate">
@@ -217,8 +218,8 @@
</div>
<div *ngIf="loading"
[class.adf-datatable-row]="display === 'list'"
[class.adf-data-table-card-loading]="display === 'gallery'">
<div class="adf-no-content-container adf-datatable-table-cell">
[class.adf-datatable-card-loading]="display === 'gallery'">
<div class="adf-no-content-container adf-datatable-cell">
<ng-template *ngIf="loadingTemplate"
ngFor [ngForOf]="[data]"
[ngForTemplate]="loadingTemplate">

View File

@@ -20,7 +20,7 @@
$data-table-cell-top: $data-table-card-padding / 2;
$data-table-drag-border: 1px dashed rgb(68, 138, 255);
.adf-data-table-card {
.adf-datatable-card {
border: 1px solid mat-color($foreground, divider);
@@ -68,34 +68,34 @@
padding-bottom: 31px;
}
.adf-data-table-card-permission {
.adf-datatable-card-permission {
width: 100%;
min-height: 250px;
.adf-datatable-table-cell {
.adf-datatable-cell {
height: 240px !important;
}
}
.adf-data-table-card-loading {
.adf-datatable-card-loading {
width: 100%;
min-height: 250px;
.adf-datatable-table-cell {
.adf-datatable-cell {
height: 240px !important;
}
}
.adf-data-table-card-empty {
.adf-datatable-card-empty {
width: 100%;
min-height: 380px;
.adf-datatable-table-cell {
.adf-datatable-cell {
height: 370px !important;
}
}
.adf-datatable-table-cell {
.adf-datatable-cell {
text-align: left;
flex: 0 1 24%;
height: 136px !important;
@@ -123,7 +123,6 @@
height: 42px !important;
width: 42px !important;
right: 0;
text-align: right;
}
.adf-image-table-cell {
@@ -134,7 +133,7 @@
border-bottom-width: 1px;
border-bottom-style: solid;
.adf-cell-container {
.adf-datatable-cell-container {
float: left;
width: 42px;
}
@@ -150,7 +149,7 @@
}
}
.adf-datatable-table-checkbox {
.adf-datatable-checkbox {
margin: 8px;
}
@@ -163,65 +162,27 @@
}
.adf-data-table {
display: table;
width: 100%;
position: relative;
border: $data-table-dividers;
// border-collapse: collapse;
white-space: nowrap;
font-size: $data-table-font-size;
.adf-datatable-list {
display: flex;
flex-direction: column;
background-color: mat-color($background, card);
/* Firefox fixes */
border-collapse: unset;
border-spacing: 0;
.adf-datatable-link {
text-decoration: none;
color: mat-color($foreground, text);
&:hover {
color: #2196f3;
text-decoration: underline;
}
}
.adf-datatable-row {
display: table-row;
vertical-align: inherit;
border-color: inherit;
}
.adf-datatable-body {
display: table-row-group;
vertical-align: middle;
border-color: inherit;
}
.adf-datatable-table-cell {
display: table-cell;
}
.adf-datatable-table-cell-header {
display: table-cell;
}
border: $data-table-dividers;
.adf-datatable-header {
padding-bottom: 3px;
display: table-header-group;
vertical-align: middle;
border-color: inherit;
display: flex;
flex-direction: column;
}
.adf-datatable-body {
display: flex;
flex-direction: column;
.adf-datatable-row {
cursor: pointer;
position: relative;
height: $data-table-row-height;
@include material-animation-default(0.28s);
transition-property: background-color;
border-top: $data-table-dividers;
padding-top: 12px;
padding-bottom: 12px;
&:hover {
background-color: $data-table-hover-color;
@@ -230,55 +191,49 @@
&.adf-is-selected, &.adf-is-selected:hover {
background-color: $data-table-selection-color;
}
&:focus {
outline-offset: -1px;
outline-width: 1px;
outline-color: rgb(68, 138, 255);
outline-style: solid;
}
&:last-child {
& > td {
border-bottom: $data-table-dividers;
}
}
}
}
.adf-datatable-table-cell, .adf-datatable-table-cell-header {
padding: 0 $data-table-column-padding 12px $data-table-column-padding;
.adf-datatable-row {
display: flex;
align-items: center;
padding-left: 20px;
padding-right: 20px;
.adf-datatable-checkbox {
max-width: 50px;
}
}
.adf-datatable-cell {
text-align: left;
&:first-of-type {
padding-left: 24px;
&--text {
text-align: left;
}
&:last-of-type {
&--date {
text-align: left;
}
&--number {
text-align: right;
}
&--image {
padding-left: 24px;
padding-right: 24px;
width: 10px;
text-align: left;
}
&:focus {
outline-offset: -1px;
outline-width: 1px;
outline-color: rgb(68, 138, 255);
outline-style: solid;
outline-width: 0;
}
}
.adf-datatable-table-cell {
color: mat-color($foreground, text);
position: relative;
vertical-align: middle;
height: $data-table-row-height;
border-top: $data-table-dividers;
padding-top: $data-table-cell-top;
box-sizing: border-box;
@include adf-no-select;
}
.adf-datatable-table-cell-header {
.adf-datatable-cell-header {
@include adf-no-select;
cursor: pointer;
position: relative;
@@ -299,10 +254,13 @@
&:hover {
cursor: pointer;
}
padding-top: 12px;
display: flex;
align-items: center;
}
&.adf-data-table__header--sorted-asc,
&.adf-data-table__header--sorted-desc {
&.adf-datatable__header--sorted-asc,
&.adf-datatable__header--sorted-desc {
color: $data-table-header-sorted-color;
&::before {
@include typo-icon;
@@ -312,48 +270,63 @@
vertical-align: sub;
}
}
&.adf-data-table__header--sorted-desc::before {
&.adf-datatable__header--sorted-desc::before {
content: '\e5db';
}
}
.adf-data-table-cell {
text-align: left;
height: 100%;
&--text {
text-align: left;
}
&--date {
text-align: left;
}
&--number {
text-align: right;
}
&--image {
padding-left: 24px;
padding-right: 24px;
width: 10px;
text-align: left;
.adf-cell-value {
height: 24px;
}
img {
height: 100%;
}
}
.adf-cell-container {
&.adf-datatable-checkbox {
display: flex;
align-items: center;
}
}
.adf-datatable-cell-header.adf-expand-cell-1, .adf-datatable-cell.adf-expand-cell-1 {
flex-grow: 1;
}
.adf-datatable-cell-header.adf-expand-cell-2, .adf-datatable-cell.adf-expand-cell-2 {
flex-grow: 2;
}
.adf-datatable-cell-header.adf-expand-cell-3, .adf-datatable-cell.adf-expand-cell-3 {
flex-grow: 3;
}
.adf-datatable-cell-header.adf-expand-cell-4, .adf-datatable-cell.adf-expand-cell-4 {
flex-grow: 4;
}
.adf-datatable-cell-header.adf-expand-cell-5, .adf-datatable-cell.adf-expand-cell-5 {
flex-grow: 5;
}
.adf-datatable-cell, .adf-datatable-cell-header {
flex: 1;
padding: 0;
.adf-datatable-cell-container {
overflow: hidden;
}
.adf-datatable-cell-value {
overflow: hidden;
text-overflow: ellipsis;
}
}
.adf-cell-value {
display: flex;
}
.adf-datatable__actions-cell, .adf-datatable-cell--image {
max-width: 50px;
display: flex;
}
.adf-datatable-cell--image {
max-width: 50px;
}
.adf-location-cell {
a {
text-decoration: none;
@@ -366,8 +339,71 @@
}
}
.adf-full-width {
width: 100%;
/* [Accessibility] For screen reader only */
.adf-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.adf-is-selected {
background: mat-color($primary, 100);
}
.adf-datatable-link {
text-decoration: none;
color: mat-color($foreground, text);
&:hover {
color: #2196f3;
text-decoration: underline;
}
}
.adf-expand-cell {
}
.adf-ellipsis-cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.adf-datatable-cell, .adf-datatable-cell-header {
overflow: hidden;
.adf-datatable-cell-container {
overflow: hidden;
}
.adf-datatable-cell-value {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
width: calc(100% - 2em);
}
}
/* query for Microsoft IE 11*/
@media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) {
.adf-cell-value {
top: 100%;
}
}
/* cell stretching content */
& > div::after {
content: attr(title);
overflow: hidden;
height: 0;
display: block;
}
}
/* Empty folder */
@@ -402,59 +438,6 @@
}
}
.adf-ellipsis-cell {
.adf-cell-container {
height: 100%;
}
.adf-cell-container > * {
display: block;
position: absolute;
max-width: calc(100% - 2em);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.2em;
}
/* visible content */
.adf-cell-value {
display: block;
position: absolute;
max-width: calc(100% - 2em);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* query for Microsoft IE 11*/
@media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) {
.adf-cell-value {
top: 100%;
}
}
/* cell stretching content */
& > div::after {
content: attr(title);
overflow: hidden;
height: 0;
display: block;
}
}
/* [Accessibility] For screen reader only */
.adf-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
/* Utils */
.adf-hidden {
display: none;
@@ -465,6 +448,10 @@
.adf-desktop-only {
display: none;
}
.adf-sticky-header {
width: 100%;
}
}
@media (max-device-width: 768px) {
@@ -472,6 +459,24 @@
display: none;
}
}
}
.adf-sticky-header {
border-top: 0;
.adf-datatable-header {
position: absolute;
background-color: mat-color($background, card);
display: flex;
z-index: 10;
border-top: $data-table-dividers;
border-bottom: $data-table-dividers;
width: calc(100% - 16em);
}
.adf-datatable-body {
margin-top: 56px;
}
}
.adf-upload__dragging {
@@ -492,7 +497,7 @@
}
}
.adf-data-table--empty {
.adf-datatable--empty {
@include flex-column;
justify-content: center;
align-items: center;

View File

@@ -120,8 +120,8 @@ describe('DataTable', () => {
fixture.detectChanges();
expect(element.querySelector('.adf-data-table-card')).not.toBeNull();
expect(element.querySelector('.adf-data-table')).toBeNull();
expect(element.querySelector('.adf-datatable-card')).not.toBeNull();
expect(element.querySelector('.adf-datatable')).toBeNull();
});
it('should use the cardview style if cardview is false', () => {
@@ -139,8 +139,8 @@ describe('DataTable', () => {
fixture.detectChanges();
expect(element.querySelector('.adf-data-table-card')).toBeNull();
expect(element.querySelector('.adf-data-table')).not.toBeNull();
expect(element.querySelector('.adf-datatable-card')).toBeNull();
expect(element.querySelector('.adf-datatable-list')).not.toBeNull();
});
it('should hide the header if showHeader is false', () => {
@@ -1021,12 +1021,12 @@ describe('Accesibility', () => {
});
fixture.detectChanges();
const datatableAttributes = element.querySelector('.adf-data-table').attributes;
const datatableHeaderAttributes = element.querySelector('.adf-data-table .adf-datatable-header').attributes;
const datatableHeaderCellAttributes = element.querySelector('.adf-datatable-table-cell-header').attributes;
const datatableAttributes = element.querySelector('.adf-datatable-list').attributes;
const datatableHeaderAttributes = element.querySelector('.adf-datatable-list .adf-datatable-header').attributes;
const datatableHeaderCellAttributes = element.querySelector('.adf-datatable-cell-header').attributes;
const datatableBodyAttributes = element.querySelector('.adf-datatable-body').attributes;
const datatableBodyRowAttributes = element.querySelector('.adf-datatable-body .adf-datatable-row').attributes;
const datatableBodyCellAttributes = element.querySelector('.adf-datatable-body .adf-datatable-table-cell').attributes;
const datatableBodyCellAttributes = element.querySelector('.adf-datatable-body .adf-datatable-cell').attributes;
expect(datatableAttributes.getNamedItem('role').value).toEqual('grid');
expect(datatableHeaderAttributes.getNamedItem('role').value).toEqual('rowgroup');

View File

@@ -119,6 +119,10 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
@Input()
showHeader: boolean = true;
/** Toggles the sticky header mode. */
@Input()
stickyHeader: boolean = false;
/** Emitted when the user clicks a row. */
@Output()
rowClick = new EventEmitter<DataRowEvent>();

View File

@@ -15,19 +15,17 @@
width: mat-font-size($config, display-3) !important;
}
p {
line-height: 0;
}
&__title {
font-size: 18px;
font-weight: 600;
line-height: 0;
}
&__subtitle,
&__text {
font-size: 14px;
font-weight: 300;
line-height: inherit;
}
}
}

View File

@@ -1,8 +1,8 @@
@mixin adf-task-people-list-theme($theme) {
.adf-task-people-list {
.adf-data-table {
width: 0;
.adf-datatable-list {
width: 100%;
}
}

View File

@@ -60,7 +60,7 @@
display: none;
}
.adf-data-table .adf-data-table-cell .adf-cell-container {
.adf-datatable .adf-datatable-cell .adf-cell-container {
flex-direction: column;
align-items: left;
}