Virtual scroll

This commit is contained in:
Adina Țeudan 2021-02-09 04:27:45 +02:00 committed by Timo
parent 3f3cae619e
commit 4fa57130fd
19 changed files with 274 additions and 230 deletions

View File

@ -110,6 +110,7 @@ import { DefaultColorsScreenComponent } from './screens/admin/default-colors-scr
import { EditColorDialogComponent } from './screens/admin/default-colors-screen/edit-color-dialog/edit-color-dialog.component';
import { DownloadsListScreenComponent } from './screens/downloads-list-screen/downloads-list-screen.component';
import { DigitalSignatureScreenComponent } from './screens/admin/digital-signature-screen/digital-signature-screen.component';
import { ScrollingModule } from '@angular/cdk/scrolling';
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -397,7 +398,8 @@ const matImports = [
FileUploadDownloadModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
ColorPickerModule,
AceEditorModule
AceEditorModule,
ScrollingModule
],
providers: [
{

View File

@ -34,9 +34,9 @@
<div class="scrollbar-placeholder"></div>
</div>
<div class="grid-container" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div class="table-item" *ngFor="let color of colors | sortBy: sortingOption.order:sortingOption.column">
<div class="table-item" *cdkVirtualFor="let color of colors | sortBy: sortingOption.order:sortingOption.column">
<div>
<div class="table-item-title heading" [translate]="'default-colors-screen.types.' + color.key"></div>
</div>
@ -59,7 +59,7 @@
</div>
<div class="scrollbar-placeholder"></div>
</div>
</div>
</cdk-virtual-scroll-viewport>
</div>
</div>
</section>

View File

@ -1,26 +1,30 @@
.left-container {
width: 100vw;
.grid-container {
grid-template-columns: 2fr 1fr 2fr 11px;
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 2fr 11px;
&.has-scrollbar:hover {
grid-template-columns: 2fr 1fr 2fr;
.table-item {
> div:not(.scrollbar-placeholder) {
padding-left: 24px;
}
.color-wrapper {
align-items: center;
.color-square {
width: 16px;
height: 16px;
min-width: 16px;
}
}
}
}
.table-item {
> div:not(.scrollbar-placeholder) {
padding-left: 24px;
}
.color-wrapper {
align-items: center;
.color-square {
width: 16px;
height: 16px;
min-width: 16px;
}
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 2fr;
}
}
}

View File

@ -93,11 +93,10 @@
<div class="scrollbar-placeholder"></div>
</div>
<div class="grid-container" redactionHasScrollbar>
<!-- Table lines -->
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
class="table-item pointer"
*ngFor="let dict of displayedDictionaries | sortBy: sortingOption.order:sortingOption.column"
*cdkVirtualFor="let dict of displayedDictionaries | sortBy: sortingOption.order:sortingOption.column"
[routerLink]="[dict.type]"
>
<div class="pr-0" (click)="toggleDictSelected($event, dict)">
@ -155,7 +154,7 @@
</div>
<div class="scrollbar-placeholder"></div>
</div>
</div>
</cdk-virtual-scroll-viewport>
</ng-container>
</div>

View File

@ -24,44 +24,48 @@ redaction-table-col-name::ng-deep {
.left-container {
width: calc(100vw - 353px);
.grid-container {
grid-template-columns: auto 2fr 1fr 1fr 1fr 11px;
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr 11px;
&.has-scrollbar:hover {
grid-template-columns: auto 2fr 1fr 1fr 1fr;
}
.table-item {
> div:not(.scrollbar-placeholder) {
display: flex;
flex-direction: row;
padding-left: 10px;
align-items: center;
justify-content: flex-start;
.table-item {
> div:not(.scrollbar-placeholder) {
display: flex;
flex-direction: row;
padding-left: 10px;
align-items: center;
justify-content: flex-start;
&.analyzed,
&.rank {
justify-content: center;
}
&.analyzed,
&.rank {
justify-content: center;
}
.color-square {
width: 16px;
height: 16px;
min-width: 16px;
margin-right: 16px;
}
.color-square {
width: 16px;
height: 16px;
min-width: 16px;
margin-right: 16px;
}
.dict-name {
z-index: 1;
max-width: 100%;
}
.dict-name {
z-index: 1;
max-width: 100%;
}
.stats-subtitle {
margin-top: 4px;
.stats-subtitle {
margin-top: 4px;
}
}
}
}
}
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr;
}
}
}
.right-container {

View File

@ -7,6 +7,7 @@
}
.grid-container {
display: grid;
grid-template-columns: 1fr 2fr;
margin: 20px;

View File

@ -87,11 +87,10 @@
<div class="scrollbar-placeholder"></div>
</div>
<div class="grid-container" redactionHasScrollbar>
<!-- Table lines -->
<cdk-virtual-scroll-viewport [itemSize]="100" redactionHasScrollbar>
<div
class="table-item pointer"
*ngFor="let ruleSet of displayedRuleSets | sortBy: sortingOption.order:sortingOption.column"
*cdkVirtualFor="let ruleSet of displayedRuleSets | sortBy: sortingOption.order:sortingOption.column"
[routerLink]="[ruleSet.ruleSetId, 'dictionaries']"
>
<div class="pr-0" (click)="toggleTemplateSelected($event, ruleSet)">
@ -135,7 +134,7 @@
<div class="scrollbar-placeholder"></div>
</div>
</div>
</cdk-virtual-scroll-viewport>
</div>
</div>
</section>

View File

@ -14,40 +14,44 @@ redaction-table-col-name::ng-deep {
.left-container {
width: 100vw;
.grid-container {
grid-template-columns: auto 1fr 1fr 1fr 1fr 11px;
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 1fr 1fr 1fr 1fr 11px;
&.has-scrollbar:hover {
grid-template-columns: auto 1fr 1fr 1fr 1fr;
.table-item {
> div:not(.scrollbar-placeholder) {
display: flex;
flex-direction: row;
padding-left: 10px;
align-items: center;
justify-content: flex-start;
&.template-name {
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
.stats-subtitle {
margin-top: 4px;
}
.table-item-title {
max-width: 100%;
}
&.created-by,
&.created-on,
&.modified-on {
display: flex;
}
}
}
}
.table-item {
> div:not(.scrollbar-placeholder) {
display: flex;
flex-direction: row;
padding-left: 10px;
align-items: center;
justify-content: flex-start;
&.template-name {
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
.stats-subtitle {
margin-top: 4px;
}
.table-item-title {
max-width: 100%;
}
&.created-by,
&.created-on,
&.modified-on {
display: flex;
}
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 1fr 1fr 1fr 1fr;
}
}
}

View File

@ -37,16 +37,16 @@
<div class="scrollbar-placeholder"></div>
</div>
<div class="grid-container" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div class="table-item" *ngFor="let user of displayedUsers">
<div class="table-item" *cdkVirtualFor="let user of displayedUsers">
<div>
<redaction-initials-avatar [userId]="user.userId" [withName]="true" size="large"></redaction-initials-avatar>
</div>
<div>{{ user.email || '-' }}</div>
<div class="scrollbar-placeholder"></div>
</div>
</div>
</cdk-virtual-scroll-viewport>
</div>
<div class="right-container"></div>

View File

@ -1,16 +1,20 @@
.left-container {
width: calc(100vw - 353px);
.grid-container {
grid-template-columns: 1fr 1fr 11px;
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 1fr 1fr 11px;
&.has-scrollbar:hover {
grid-template-columns: 1fr 1fr;
.table-item {
> div {
padding: 0 24px;
}
}
}
.table-item {
> div {
padding: 0 24px;
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 1fr 1fr;
}
}
}

View File

@ -23,9 +23,9 @@
<div *ngIf="noData" class="no-data heading-l" translate="downloads-list.no-data"></div>
<div class="grid-container" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div *ngFor="let download of fileDownloadService.downloads" class="table-item">
<div *cdkVirtualFor="let download of fileDownloadService.downloads" class="table-item">
<div>
<div class="table-item-title heading">{{ download.filename }}</div>
</div>
@ -58,7 +58,7 @@
</div>
<div class="scrollbar-placeholder"></div>
</div>
</div>
</cdk-virtual-scroll-viewport>
</div>
</div>
</section>

View File

@ -1,16 +1,20 @@
.left-container {
width: 100vw;
.grid-container {
grid-template-columns: 2fr 1fr 1fr auto 11px;
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 1fr auto 11px;
&.has-scrollbar:hover {
grid-template-columns: 2fr 1fr 1fr auto;
.table-item {
> div:not(.scrollbar-placeholder) {
padding-left: 24px;
}
}
}
.table-item {
> div:not(.scrollbar-placeholder) {
padding-left: 24px;
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 1fr auto;
}
}
}

View File

@ -76,9 +76,9 @@
<div *ngIf="noData" class="no-data heading-l" translate="project-listing.no-projects-match"></div>
<div class="grid-container" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="100" redactionHasScrollbar>
<div
*ngFor="let pw of displayedProjects | sortBy: sortingOption.order:sortingOption.column"
*cdkVirtualFor="let pw of displayedProjects | sortBy: sortingOption.order:sortingOption.column"
[class.pointer]="canOpenProject(pw)"
[routerLink]="[canOpenProject(pw) ? '/ui/projects/' + pw.project.projectId : []]"
class="table-item"
@ -125,7 +125,7 @@
</div>
<div class="scrollbar-placeholder"></div>
</div>
</div>
</cdk-virtual-scroll-viewport>
</div>
<div class="right-container" redactionHasScrollbar>

View File

@ -2,27 +2,31 @@
@import '../../../assets/styles/red-variables';
.left-container {
.grid-container {
grid-template-columns: 2fr 1fr 1fr auto 11px;
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 1fr auto 11px;
.table-item {
> div {
height: 100px;
padding: 0 24px;
}
.status-container {
width: 160px;
padding-right: 13px;
}
}
.stats-subtitle {
margin-top: 6px;
}
}
&.has-scrollbar:hover {
grid-template-columns: 2fr 1fr 1fr auto;
}
.table-item {
> div {
height: 100px;
padding: 0 24px;
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 1fr auto;
}
.status-container {
width: 160px;
padding-right: 13px;
}
}
.stats-subtitle {
margin-top: 6px;
}
}

View File

@ -169,9 +169,9 @@
<div *ngIf="noData" class="no-data heading-l" translate="project-overview.no-files-match"></div>
<div redactionHasScrollbar class="grid-container">
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
*ngFor="let fileStatus of displayedFiles | sortBy: sortingOption.order:sortingOption.column; trackBy: fileId"
*cdkVirtualFor="let fileStatus of displayedFiles | sortBy: sortingOption.order:sortingOption.column; trackBy: fileId"
[class.pointer]="permissionsService.canOpenFile(fileStatus)"
[routerLink]="fileLink(fileStatus)"
class="table-item"
@ -242,7 +242,7 @@
</div>
<div class="scrollbar-placeholder"></div>
</div>
</div>
</cdk-virtual-scroll-viewport>
</ng-container>
</div>

View File

@ -15,55 +15,59 @@ redaction-table-col-name::ng-deep {
}
}
.grid-container {
grid-template-columns: auto 3fr 2fr 1fr 2fr 1fr auto 11px;
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 3fr 2fr 1fr 2fr 1fr auto 11px;
&.has-scrollbar:hover {
grid-template-columns: auto 3fr 2fr 1fr 2fr 1fr auto;
}
.table-item {
> div {
padding-left: 10px;
}
.table-item {
> div {
padding-left: 10px;
}
.disabled {
color: $grey-7;
}
.disabled {
color: $grey-7;
}
.error {
color: $red-1;
}
.error {
color: $red-1;
}
.extend-cols {
grid-column-end: span 3;
align-items: flex-end;
}
.extend-cols {
grid-column-end: span 3;
align-items: flex-end;
}
.table-item-title {
max-width: 25vw;
}
.table-item-title {
max-width: 25vw;
}
.pages {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
opacity: 0.7;
color: $grey-1;
font-size: 11px;
letter-spacing: 0;
line-height: 14px;
.pages {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
opacity: 0.7;
color: $grey-1;
font-size: 11px;
letter-spacing: 0;
line-height: 14px;
.mat-icon {
width: 10px;
height: 10px;
margin-right: 4px;
}
}
.mat-icon {
width: 10px;
height: 10px;
margin-right: 4px;
.status-container {
align-items: flex-end;
}
}
}
.status-container {
align-items: flex-end;
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 3fr 2fr 1fr 2fr 1fr auto;
}
}
}

View File

@ -1,4 +1,5 @@
import { AfterContentChecked, Directive, ElementRef, HostBinding } from '@angular/core';
import { debounce } from './debounce';
@Directive({
selector: '[redactionHasScrollbar]',
@ -10,7 +11,16 @@ export class HasScrollbarDirective implements AfterContentChecked {
@HostBinding('class') class = '';
ngAfterContentChecked() {
this.class = this.hasScrollbar ? 'has-scrollbar' : '';
this._process();
}
@debounce(10)
_process() {
const newClass = this.hasScrollbar ? 'has-scrollbar' : '';
if (this.class !== newClass) {
this.class = newClass;
}
}
public get hasScrollbar() {

View File

@ -31,12 +31,14 @@ export class SyncWidthDirective implements AfterViewChecked {
return { tableRow, length };
}
@debounce(0)
@debounce(10)
matchWidth() {
const headerItems = this.el.nativeElement.children;
const tableRows = document.getElementsByClassName(this.redactionSyncWidth);
if (!tableRows || !tableRows.length) return;
if (!tableRows || !tableRows.length) {
return;
}
const { tableRow, length } = this._sampleRow;

View File

@ -21,76 +21,79 @@
}
}
.grid-container {
display: grid;
max-height: calc(100vh - 50px - 30px - 111px);
overflow-y: hidden;
cdk-virtual-scroll-viewport {
height: calc(100vh - 50px - 31px - 111px);
overflow-y: hidden !important;
.table-item {
display: contents;
.cdk-virtual-scroll-content-wrapper {
display: grid;
> div {
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
box-sizing: border-box;
height: 80px;
border-bottom: 1px solid $separator;
padding: 0 13px;
.table-item {
display: contents;
&:not(.scrollbar-placeholder):not(.pr-0) {
min-width: 110px;
}
}
.table-item-title {
font-weight: 600;
@include line-clamp(1);
}
.action-buttons {
position: absolute;
display: none;
right: -11px;
top: 0;
height: 100%;
width: fit-content;
flex-direction: row;
align-items: center;
padding-left: 100px;
padding-right: 24px;
z-index: 1;
background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, #f4f5f7 35%);
mat-icon {
width: 14px;
}
&.active {
display: flex;
// compensate for scroll
padding-right: 23px;
}
}
&:hover {
> div {
background-color: #f9fafb;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
box-sizing: border-box;
height: 80px;
border-bottom: 1px solid $separator;
padding: 0 13px;
&:not(.scrollbar-placeholder):not(.pr-0) {
min-width: 110px;
}
}
.select-oval {
opacity: 1;
.table-item-title {
font-weight: 600;
@include line-clamp(1);
}
.action-buttons {
display: flex;
position: absolute;
display: none;
right: -11px;
top: 0;
height: 100%;
width: fit-content;
flex-direction: row;
align-items: center;
padding-left: 100px;
padding-right: 24px;
z-index: 1;
background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, #f4f5f7 35%);
mat-icon {
width: 14px;
}
&.active {
display: flex;
// compensate for scroll
padding-right: 23px;
}
}
&:hover {
> div {
background-color: #f9fafb;
}
.select-oval {
opacity: 1;
}
.action-buttons {
display: flex;
}
}
}
}
&:hover {
overflow-y: auto;
overflow-y: auto !important;
@include scroll-bar;
&.has-scrollbar {