Table component WIP - dictionary listing

This commit is contained in:
Adina Țeudan 2021-08-27 00:23:47 +03:00
parent 50870c7ba6
commit fbd55abf6c
42 changed files with 198 additions and 475 deletions

View File

@ -20,7 +20,7 @@
icon="red:download"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" iqserHasScrollbar>
<!-- Table lines -->
<div *cdkVirtualFor="let download of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey" class="table-item">
<div (click)="toggleEntitySelected($event, download)" class="selection-column">
@ -53,15 +53,15 @@
(action)="downloadItem(download)"
*ngIf="download.status === 'READY' && !download.inProgress"
[tooltip]="'downloads-list.actions.download' | translate"
icon="red:download"
[type]="circleButtonTypes.dark"
icon="red:download"
></iqser-circle-button>
<iqser-circle-button
(action)="deleteItems([download])"
[tooltip]="'downloads-list.actions.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
<mat-spinner *ngIf="download.inProgress" diameter="15"></mat-spinner>
@ -79,7 +79,7 @@
(action)="deleteItems()"
*ngIf="entitiesService.areSomeSelected$ | async"
[tooltip]="'downloads-list.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
</ng-template>

View File

@ -63,8 +63,8 @@
<textarea
[placeholder]="'add-edit-dictionary.form.description-placeholder' | translate"
formControlName="description"
iqserHasScrollbar
name="description"
redactionHasScrollbar
rows="4"
type="text"
></textarea>

View File

@ -12,7 +12,7 @@
icon="red:attribute"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="50" iqserHasScrollbar>
<div
(mouseenter)="setHoveredColumn.emit(field.csvColumn)"
(mouseleave)="setHoveredColumn.emit()"

View File

@ -113,7 +113,7 @@
icon="red:document"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="80" iqserHasScrollbar>
<div *cdkVirtualFor="let log of logs?.data" class="table-item">
<div>
{{ log.message }}

View File

@ -26,7 +26,7 @@
[tableHeaderLabel]="tableHeaderLabel"
></iqser-table-header>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="80" iqserHasScrollbar>
<div *cdkVirtualFor="let color of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey" class="table-item">
<div>
<div [translate]="translations[color.key]" class="table-item-title heading"></div>
@ -42,8 +42,8 @@
(action)="openEditColorDialog($event, color)"
*ngIf="currentUser.isAdmin"
[tooltip]="'default-colors-screen.action.edit' | translate"
icon="iqser:edit"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
></iqser-circle-button>
</div>
</div>

View File

@ -20,88 +20,33 @@
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container">
<iqser-table-header
<iqser-table
[actionsTemplate]="actionsTemplate"
[bulkActions]="bulkActions"
[itemSize]="80"
[routerLinkFn]="routerLinkFn"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></iqser-table-header>
emptyColumnWidth="1fr"
></iqser-table>
<redaction-empty-state
(action)="openAddEditDictionaryDialog()"
*ngIf="entitiesService.noData$ | async"
[buttonLabel]="'dictionary-listing.no-data.action' | translate"
[showButton]="currentUser.isAdmin"
[text]="'dictionary-listing.no-data.title' | translate"
icon="red:dictionary"
></redaction-empty-state>
<!-- <redaction-empty-state-->
<!-- (action)="openAddEditDictionaryDialog()"-->
<!-- *ngIf="entitiesService.noData$ | async"-->
<!-- [buttonLabel]="'dictionary-listing.no-data.action' | translate"-->
<!-- [showButton]="currentUser.isAdmin"-->
<!-- [text]="'dictionary-listing.no-data.title' | translate"-->
<!-- icon="red:dictionary"-->
<!-- ></redaction-empty-state>-->
<redaction-empty-state
*ngIf="noMatch$ | async"
[text]="'dictionary-listing.no-match.title' | translate"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
*cdkVirtualFor="let dict of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[routerLink]="[dict.type]"
class="table-item pointer"
>
<div (click)="toggleEntitySelected($event, dict)" class="selection-column">
<iqser-round-checkbox [active]="isSelected(dict)"></iqser-round-checkbox>
</div>
<div>
<div [ngStyle]="{ 'background-color': dict.hexColor }" class="color-square"></div>
<div class="dict-name">
<div class="table-item-title heading">
{{ dict.label }}
</div>
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:entries"></mat-icon>
{{ dict.entries?.length }}
</div>
<div *ngIf="!dict.caseInsensitive">
<mat-icon svgIcon="red:case-sensitive"></mat-icon>
{{ 'dictionary-listing.case-sensitive' | translate }}
</div>
</div>
</div>
</div>
<div class="center small-label">
{{ dict.rank }}
</div>
<div class="center">
<redaction-annotation-icon [dictType]="dict" [type]="dict.hint ? 'circle' : 'square'"></redaction-annotation-icon>
</div>
<div class="actions-container">
<div *ngIf="currentUser.isAdmin" class="action-buttons">
<iqser-circle-button
(action)="openDeleteDictionariesDialog($event, [dict])"
[tooltip]="'dictionary-listing.action.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<iqser-circle-button
(action)="openAddEditDictionaryDialog($event, dict)"
[tooltip]="'dictionary-listing.action.edit' | translate"
icon="iqser:edit"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</div>
</div>
<div class="scrollbar-placeholder"></div>
</div>
</cdk-virtual-scroll-viewport>
<!-- <redaction-empty-state-->
<!-- *ngIf="noMatch$ | async"-->
<!-- [text]="'dictionary-listing.no-match.title' | translate"-->
<!-- ></redaction-empty-state>-->
</div>
<div class="right-container" redactionHasScrollbar>
<div class="right-container" iqserHasScrollbar>
<redaction-simple-doughnut-chart
*ngIf="(entitiesService.noData$ | async) === false"
[config]="chartData"
@ -120,8 +65,8 @@
(action)="openDeleteDictionariesDialog($event)"
*ngIf="currentUser.isAdmin && (entitiesService.areSomeSelected$ | async)"
[tooltip]="'dictionary-listing.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
<div class="attributes-actions-container">
@ -134,9 +79,60 @@
(action)="openAddEditDictionaryDialog()"
*ngIf="currentUser.isAdmin"
[label]="'dictionary-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
icon="red:plus"
></iqser-icon-button>
</div>
</div>
</ng-template>
<ng-template #labelTemplate let-entity="entity">
<div class="cell">
<div [ngStyle]="{ 'background-color': entity.hexColor }" class="color-square"></div>
<div class="dict-name">
<div class="table-item-title heading">
{{ entity.label }}
</div>
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:entries"></mat-icon>
{{ entity.entries?.length }}
</div>
<div *ngIf="!entity.caseInsensitive">
<mat-icon svgIcon="red:case-sensitive"></mat-icon>
{{ 'dictionary-listing.case-sensitive' | translate }}
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #rankTemplate let-entity="entity">
<div class="cell center small-label">
{{ entity.rank }}
</div>
</ng-template>
<ng-template #iconTemplate let-entity="entity">
<div class="cell center">
<redaction-annotation-icon [dictType]="entity" [type]="entity.hint ? 'circle' : 'square'"></redaction-annotation-icon>
</div>
</ng-template>
<ng-template #actionsTemplate let-entity="entity">
<div *ngIf="currentUser.isAdmin" class="action-buttons">
<iqser-circle-button
(action)="openDeleteDictionariesDialog($event, [entity])"
[tooltip]="'dictionary-listing.action.delete' | translate"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
<iqser-circle-button
(action)="openAddEditDictionaryDialog($event, entity)"
[tooltip]="'dictionary-listing.action.edit' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
></iqser-circle-button>
</div>
</ng-template>

View File

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

View File

@ -1,4 +1,4 @@
import { Component, Injector, OnInit } from '@angular/core';
import { Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { DictionaryControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
@ -29,29 +29,18 @@ const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
@Component({
templateUrl: './dictionary-listing-screen.component.html',
styleUrls: ['./dictionary-listing-screen.component.scss'],
providers: [...DefaultListingServices]
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DictionaryListingScreenComponent) }]
})
export class DictionaryListingScreenComponent extends ListingComponent<TypeValueWrapper> implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dictionary-listing.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<TypeValueWrapper>[] = [
{
label: _('dictionary-listing.table-col-names.type'),
sortByKey: 'label'
},
{
label: _('dictionary-listing.table-col-names.order-of-importance'),
sortByKey: 'rank',
class: 'flex-center'
},
{
label: _('dictionary-listing.table-col-names.hint-redaction'),
class: 'flex-center'
}
];
tableColumnConfigs: TableColumnConfig<TypeValueWrapper>[];
chartData: DoughnutChartConfig[] = [];
@ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef<never>;
@ViewChild('rankTemplate', { static: true }) rankTemplate: TemplateRef<never>;
@ViewChild('iconTemplate', { static: true }) iconTemplate: TemplateRef<never>;
protected readonly _primaryKey = 'type';
constructor(
@ -69,7 +58,10 @@ export class DictionaryListingScreenComponent extends ListingComponent<TypeValue
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
}
routerLinkFn = (entity: TypeValueWrapper) => [entity.type];
ngOnInit(): void {
this._setColumnConfig();
this._loadDictionaryData();
}
@ -108,6 +100,30 @@ export class DictionaryListingScreenComponent extends ListingComponent<TypeValue
);
}
private _setColumnConfig() {
this.tableColumnConfigs = [
{
label: _('dictionary-listing.table-col-names.type'),
sortByKey: 'label',
width: '2fr',
template: this.labelTemplate
},
{
label: _('dictionary-listing.table-col-names.order-of-importance'),
sortByKey: 'rank',
class: 'flex-center',
width: '1fr',
template: this.rankTemplate
},
{
label: _('dictionary-listing.table-col-names.hint-redaction'),
class: 'flex-center',
width: '1fr',
template: this.iconTemplate
}
];
}
private _loadDictionaryData(loadEntries = true): void {
const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeDossierTemplateId];
const entities = Object.values(appStateDictionaryData).filter(d => !d.virtual);

View File

@ -22,8 +22,8 @@
<div class="content-container">
<iqser-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></iqser-table-header>
@ -42,7 +42,7 @@
[text]="'dossier-attributes-listing.no-match.title' | translate"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="50" iqserHasScrollbar>
<div
*cdkVirtualFor="let attribute of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
class="table-item pointer"
@ -67,15 +67,15 @@
<iqser-circle-button
(action)="openAddEditAttributeDialog($event, attribute)"
[tooltip]="'dossier-attributes-listing.action.edit' | translate"
icon="iqser:edit"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="openConfirmDeleteAttributeDialog($event, attribute)"
[tooltip]="'dossier-attributes-listing.action.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
</div>
</div>
@ -91,8 +91,8 @@
(action)="openConfirmDeleteAttributeDialog($event)"
*ngIf="currentUser.isAdmin && entitiesService.areSomeSelected$ | async"
[tooltip]="'dossier-attributes-listing.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
<div class="attributes-actions-container">
@ -105,8 +105,8 @@
(action)="openAddEditAttributeDialog($event)"
*ngIf="currentUser.isAdmin"
[label]="'dossier-attributes-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
icon="red:plus"
></iqser-icon-button>
</div>
</ng-template>

View File

@ -45,7 +45,7 @@
[text]="'dossier-templates-listing.no-match.title' | translate"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="80" iqserHasScrollbar>
<div
*cdkVirtualFor="let dossierTemplate of sortedDisplayedEntities$ | async"
[routerLink]="[dossierTemplate.dossierTemplateId, 'dictionaries']"

View File

@ -22,8 +22,8 @@
<div class="content-container">
<iqser-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></iqser-table-header>
@ -39,7 +39,7 @@
[text]="'file-attributes-listing.no-match.title' | translate"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="80" iqserHasScrollbar>
<div *cdkVirtualFor="let attribute of sortedDisplayedEntities$ | async" class="table-item">
<div (click)="toggleEntitySelected($event, attribute)" class="selection-column">
<iqser-round-checkbox [active]="isSelected(attribute)"></iqser-round-checkbox>
@ -76,14 +76,14 @@
<iqser-circle-button
(action)="openAddEditAttributeDialog($event, attribute)"
[tooltip]="'file-attributes-listing.action.edit' | translate"
icon="iqser:edit"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="openConfirmDeleteAttributeDialog($event, attribute)"
[tooltip]="'file-attributes-listing.action.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
</div>
</div>
@ -101,8 +101,8 @@
(click)="openConfirmDeleteAttributeDialog($event)"
*ngIf="currentUser.isAdmin && (entitiesService.areSomeSelected$ | async)"
[tooltip]="'file-attributes-listing.bulk-actions.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
<div class="attributes-actions-container">
@ -117,17 +117,17 @@
(action)="fileInput.click()"
*ngIf="currentUser.isAdmin"
[tooltip]="'file-attributes-listing.upload-csv' | translate"
[type]="circleButtonTypes.dark"
icon="red:upload"
tooltipPosition="above"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<iqser-icon-button
(action)="openAddEditAttributeDialog($event)"
*ngIf="currentUser.isAdmin"
[label]="'file-attributes-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
icon="red:plus"
></iqser-icon-button>
</div>
</ng-template>

View File

@ -19,7 +19,7 @@
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container" redactionHasScrollbar>
<div class="content-container" iqserHasScrollbar>
<div class="heading-xl" translate="reports-screen.title"></div>
<div class="description" translate="reports-screen.description"></div>
@ -42,7 +42,7 @@
</div>
</div>
<div class="right-container" redactionHasScrollbar>
<div class="right-container" iqserHasScrollbar>
<div class="header">
<div class="heading" translate="reports-screen.report-documents"></div>
<iqser-circle-button

View File

@ -24,7 +24,7 @@
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'trash.no-match.title' | translate"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" iqserHasScrollbar>
<div *cdkVirtualFor="let entity of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey" class="table-item">
<div (click)="toggleEntitySelected($event, entity)" class="selection-column">
<iqser-round-checkbox [active]="isSelected(entity)"></iqser-round-checkbox>

View File

@ -16,8 +16,8 @@
(action)="openAddEditUserDialog($event)"
*ngIf="currentUser.isUserAdmin"
[label]="'user-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
icon="red:plus"
></iqser-icon-button>
<iqser-circle-button
*ngIf="currentUser.isUser"
@ -34,15 +34,15 @@
<div [class.extended]="collapsedDetails" class="content-container">
<iqser-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></iqser-table-header>
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'user-listing.no-match.title' | translate"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<cdk-virtual-scroll-viewport [itemSize]="80" iqserHasScrollbar>
<!-- Table lines -->
<div *cdkVirtualFor="let user of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey" class="table-item">
<div (click)="toggleEntitySelected($event, user)" class="selection-column">
@ -66,15 +66,15 @@
<iqser-circle-button
(action)="openAddEditUserDialog($event, user)"
[tooltip]="'user-listing.action.edit' | translate"
icon="iqser:edit"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="openDeleteUsersDialog([user], $event)"
[disabled]="user.id === userService.currentUser.id"
[tooltip]="'user-listing.action.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
icon="red:trash"
></iqser-circle-button>
</div>
</div>
@ -83,7 +83,7 @@
</cdk-virtual-scroll-viewport>
</div>
<div [class.collapsed]="collapsedDetails" class="right-container" redactionHasScrollbar>
<div [class.collapsed]="collapsedDetails" class="right-container" iqserHasScrollbar>
<redaction-users-stats
(toggleCollapse)="collapsedDetails = !collapsedDetails"
[chartData]="chartData"
@ -101,8 +101,8 @@
[tooltip]="
(canDeleteSelected$ | async) ? ('user-listing.bulk.delete' | translate) : ('user-listing.bulk.delete-disabled' | translate)
"
[type]="circleButtonTypes.dark"
icon="red:trash"
tooltipPosition="after"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</ng-template>

View File

@ -26,14 +26,14 @@
(action)="save()"
[disabled]="configForm.invalid"
[label]="'watermark-screen.action.save' | translate"
icon="iqser:check"
[type]="iconButtonTypes.primary"
icon="iqser:check"
></iqser-icon-button>
<div (click)="revert()" class="all-caps-label cancel" translate="watermark-screen.action.revert"></div>
</div>
</div>
<div class="right-container" redactionHasScrollbar>
<div class="right-container" iqserHasScrollbar>
<div class="heading-xl" translate="watermark-screen.title"></div>
<form (keyup)="configChanged()" [formGroup]="configForm">
<div class="iqser-input-group w-300">
@ -42,8 +42,8 @@
[placeholder]="'watermark-screen.form.text-placeholder' | translate"
class="w-full"
formControlName="text"
iqserHasScrollbar
name="text"
redactionHasScrollbar
rows="4"
type="text"
></textarea>

View File

@ -15,7 +15,7 @@
</div>
</div>
<div class="right-content" redactionHasScrollbar>
<div class="right-content" iqserHasScrollbar>
<div class="section">
<div *ngFor="let attr of fileAttributesConfig?.fileAttributeConfigs" class="attribute">
<div class="small-label">{{ attr.label }}:</div>

View File

@ -61,8 +61,8 @@
</div>
<iqser-circle-button
(action)="multiSelectActive = false"
icon="iqser:close"
[type]="circleButtonTypes.primary"
icon="iqser:close"
></iqser-circle-button>
</div>
@ -134,7 +134,7 @@
(keyup)="preventKeyDefault($event)"
[class.active-panel]="!pagesPanelActive"
class="annotations"
redactionHasScrollbar
iqserHasScrollbar
tabindex="1"
>
<ng-container *ngIf="activeViewerPage && !displayedAnnotations.get(activeViewerPage)?.length">
@ -160,16 +160,16 @@
(action)="jumpToPreviousWithAnnotations()"
[disabled]="activeViewerPage <= displayedPages[0]"
[label]="'file-preview.tabs.annotations.jump-to-previous' | translate"
icon="red:nav-prev"
[type]="iconButtonTypes.dark"
icon="red:nav-prev"
></iqser-icon-button>
<iqser-icon-button
(action)="jumpToNextWithAnnotations()"
[disabled]="activeViewerPage >= displayedPages[displayedPages.length - 1]"
[label]="'file-preview.tabs.annotations.jump-to-next' | translate"
[type]="iconButtonTypes.dark"
class="mt-8"
icon="red:nav-next"
[type]="iconButtonTypes.dark"
></iqser-icon-button>
</div>
</ng-container>

View File

@ -13,7 +13,7 @@
<div class="all-caps-label" translate="file-preview.tabs.exclude-pages.removed-from-redaction"></div>
</div>
<div class="ranges" redactionHasScrollbar>
<div class="ranges" iqserHasScrollbar>
<div *ngFor="let range of excludedPagesRanges" class="range">
<ng-container *ngIf="range.startPage === range.endPage">
{{ range.startPage }}

View File

@ -1,7 +0,0 @@
<button (click)="scroll(buttonType.top)" [hidden]="(showScrollUp$ | async) === false" class="scroll-button top pointer">
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
</button>
<button (click)="scroll(buttonType.bottom)" [hidden]="(showScrollDown$ | async) === false" class="scroll-button bottom pointer">
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
</button>

View File

@ -1,30 +0,0 @@
@import '../../../../../assets/styles/variables';
.scroll-button {
background-color: $white;
position: absolute;
right: 0;
height: 40px;
width: 44px;
border: none;
border-radius: 8px 0 0 8px;
box-shadow: -1px 1px 5px 0 rgba(40, 50, 65, 0.25);
&.bottom {
bottom: 30px;
}
&.top {
top: 100px;
mat-icon {
transform: rotate(180deg);
}
}
}
mat-icon {
width: 22px;
height: 22px;
color: $grey-7;
}

View File

@ -1,60 +0,0 @@
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { concatMap, delay, distinctUntilChanged, map, startWith } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
const ButtonTypes = {
top: 'top',
bottom: 'bottom'
} as const;
type ButtonType = keyof typeof ButtonTypes;
@Component({
selector: 'redaction-scroll-button',
templateUrl: './scroll-button.component.html',
styleUrls: ['./scroll-button.component.scss']
})
export class ScrollButtonComponent implements OnInit {
readonly buttonType = ButtonTypes;
@Input()
scrollViewport: CdkVirtualScrollViewport;
@Input()
itemSize: number;
showScrollUp$: Observable<boolean>;
showScrollDown$: Observable<boolean>;
ngOnInit() {
const scrollSize = () => this.scrollViewport.getDataLength() * this.itemSize;
const scrollIsNeeded = () => this.scrollViewport.getViewportSize() < scrollSize();
const reachedEnd = (type: ButtonType) => this.scrollViewport.measureScrollOffset(type) === 0;
const showScrollUp = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.top);
const showScrollDown = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.bottom);
const scroll$ = this.scrollViewport.elementScrolled().pipe(
startWith(''),
/** Delay first value so that we can wait for items to be rendered in viewport and get correct values */
concatMap((value, index) => (index === 0 ? of(value).pipe(delay(0)) : of(value)))
);
this.showScrollUp$ = scroll$.pipe(map(showScrollUp), distinctUntilChanged());
this.showScrollDown$ = scroll$.pipe(map(showScrollDown), distinctUntilChanged());
}
scroll(type: ButtonType): void {
const viewportSize = (this.scrollViewport?.getViewportSize() - this.itemSize) * (type === ButtonTypes.top ? -1 : 1);
const scrollOffset = this.scrollViewport?.measureScrollOffset('top');
this.scrollViewport?.scrollToOffset(scrollOffset + viewportSize, 'smooth');
}
@HostListener('document:keyup', ['$event'])
spaceAndPageDownScroll(event: KeyboardEvent): void {
if (['Space', 'PageDown'].includes(event.code) && (event.target as any).tagName === 'BODY') {
this.scroll(ButtonTypes.bottom);
} else if (['PageUp'].includes(event.code) && (event.target as any).tagName === 'BODY') {
this.scroll(ButtonTypes.top);
}
}
}

View File

@ -34,8 +34,8 @@
<textarea
[placeholder]="'add-dossier-dialog.form.description.placeholder' | translate"
formControlName="description"
iqserHasScrollbar
name="description"
redactionHasScrollbar
rows="5"
type="text"
></textarea>
@ -63,8 +63,8 @@
<div class="d-flex">
<redaction-select
[label]="'report-type.label' | translate: { length: reportTemplateIdsLength }"
[options]="availableReportTypes"
[optionTemplate]="reportTemplateOptionTemplate"
[options]="availableReportTypes"
[valueMapper]="reportTemplateValueMapper"
class="mr-16"
formControlName="reportTemplateIds"
@ -86,8 +86,8 @@
(action)="saveDossierAndAddMembers()"
[disabled]="disabled"
[label]="'add-dossier-dialog.actions.save-and-add-members' | translate"
icon="red:assign"
[type]="iconButtonTypes.dark"
icon="red:assign"
></iqser-icon-button>
</div>
</form>

View File

@ -23,7 +23,7 @@
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
<label translate="change-legal-basis-dialog.content.comment"></label>
<textarea formControlName="comment" name="comment" redactionHasScrollbar rows="4" type="text"></textarea>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
</div>
</div>

View File

@ -17,7 +17,7 @@
icon="red:document"
></redaction-empty-state>
<cdk-virtual-scroll-viewport *ngIf="(entitiesService.noData$ | async) === false" [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport *ngIf="(entitiesService.noData$ | async) === false" [itemSize]="itemSize" iqserHasScrollbar>
<div *cdkVirtualFor="let file of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey" class="table-item">
<div (click)="toggleEntitySelected($event, file)" class="selection-column">
<iqser-round-checkbox [active]="isSelected(file)"></iqser-round-checkbox>

View File

@ -30,8 +30,8 @@
<textarea
[placeholder]="'edit-dossier-dialog.general-info.form.description.placeholder' | translate"
formControlName="description"
iqserHasScrollbar
name="description"
redactionHasScrollbar
rows="5"
type="text"
></textarea>
@ -60,8 +60,8 @@
(action)="deleteDossier()"
*ngIf="permissionsService.canDeleteDossier(dossierWrapper)"
[label]="'dossier-listing.delete.action' | translate"
icon="red:trash"
[type]="iconButtonTypes.dark"
icon="red:trash"
></iqser-icon-button>
</div>
</form>

View File

@ -23,7 +23,7 @@
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
<label translate="manual-annotation.dialog.content.comment"></label>
<textarea formControlName="comment" name="comment" redactionHasScrollbar rows="4" type="text"></textarea>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
</div>
</div>

View File

@ -41,7 +41,7 @@
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
<label translate="manual-annotation.dialog.content.comment"></label>
<textarea formControlName="comment" name="comment" redactionHasScrollbar rows="4" type="text"></textarea>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
</div>
<div *ngIf="isDictionaryRequest && !isFalsePositiveRequest" class="iqser-input-group required w-300">

View File

@ -18,7 +18,7 @@
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
<label translate="recategorize-image-dialog.content.comment"></label>
<textarea formControlName="comment" name="comment" redactionHasScrollbar rows="4" type="text"></textarea>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
</div>
</div>

View File

@ -41,12 +41,12 @@
<div [class.required]="!permissionsService.isApprover()" class="iqser-input-group w-300">
<label translate="manual-annotation.dialog.content.comment"></label>
<textarea formControlName="comment" name="comment" redactionHasScrollbar rows="4" type="text"></textarea>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
</div>
</div>
<div class="dialog-actions">
<button color="primary" mat-flat-button type="submit" [disabled]="!redactionForm.valid">
<button [disabled]="!redactionForm.valid" color="primary" mat-flat-button type="submit">
{{ 'remove-annotations-dialog.confirm' | translate }}
</button>
<button (click)="deny()" color="primary" mat-flat-button>

View File

@ -41,7 +41,6 @@ import { UserPreferenceControllerService } from '@redaction/red-ui-http';
import { EditDossierDictionaryComponent } from './dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component';
import { EditDossierTeamMembersComponent } from './dialogs/edit-dossier-dialog/team-members/edit-dossier-team-members.component';
import { TeamMembersManagerComponent } from './components/team-members-manager/team-members-manager.component';
import { ScrollButtonComponent } from './components/scroll-button/scroll-button.component';
import { ChangeLegalBasisDialogComponent } from './dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
import { PageExclusionComponent } from './components/page-exclusion/page-exclusion.component';
import { RecategorizeImageDialogComponent } from './dialogs/recategorize-image-dialog/recategorize-image-dialog.component';
@ -87,7 +86,6 @@ const components = [
EditDossierTeamMembersComponent,
EditDossierAttributesComponent,
TeamMembersManagerComponent,
ScrollButtonComponent,
PageExclusionComponent,
DossierDetailsStatsComponent,
EditDossierDeletedDocumentsComponent,

View File

@ -21,7 +21,7 @@
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'dossier-listing.no-match.title' | translate"></redaction-empty-state>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" iqserHasScrollbar>
<div
*cdkVirtualFor="let dossier of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.pointer]="!!dossier"
@ -77,10 +77,10 @@
</div>
</cdk-virtual-scroll-viewport>
<redaction-scroll-button [itemSize]="itemSize" [scrollViewport]="scrollViewport"></redaction-scroll-button>
<iqser-scroll-button [itemSize]="itemSize" [scrollViewport]="scrollViewport"></iqser-scroll-button>
</div>
<div class="right-container" redactionHasScrollbar>
<div class="right-container" iqserHasScrollbar>
<redaction-dossier-listing-details
*ngIf="(entitiesService.noData$ | async) === false"
[documentsChartData]="documentsChartData"

View File

@ -7,16 +7,9 @@
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 1fr auto 11px;
.table-item {
> div {
height: 85px;
padding: 0 24px;
}
.status-container {
width: 160px;
padding-right: 13px;
}
.table-item .status-container {
width: 160px;
padding-right: 13px;
}
}

View File

@ -33,8 +33,6 @@ export class DossierListingScreenComponent
extends ListingComponent<DossierWrapper>
implements OnInit, AfterViewInit, OnDestroy, OnAttach, OnDetach
{
readonly itemSize = 85;
protected readonly _primaryKey = 'dossierName';
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
readonly buttonConfigs: readonly ButtonConfig[] = [
@ -63,11 +61,11 @@ export class DossierListingScreenComponent
class: 'flex-end'
}
];
dossiersChartData: DoughnutChartConfig[] = [];
documentsChartData: DoughnutChartConfig[] = [];
readonly itemSize = 85;
protected readonly _primaryKey = 'dossierName';
private _lastScrolledIndex: number;
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkTemplate: TemplateRef<unknown>;
@ViewChild(CdkVirtualScrollViewport)

View File

@ -17,18 +17,18 @@
*ngIf="permissionsService.displayReanalyseBtn()"
[tooltipClass]="'small ' + ((entitiesService.areSomeSelected$ | async) ? '' : 'warn')"
[tooltip]="'dossier-overview.new-rule.toast.actions.reanalyse-all' | translate"
[type]="circleButtonTypes.warn"
icon="iqser:refresh"
tooltipPosition="below"
[type]="circleButtonTypes.warn"
></iqser-circle-button>
<iqser-circle-button
(action)="fileInput.click()"
[tooltip]="'dossier-overview.header-actions.upload-document' | translate"
[type]="circleButtonTypes.primary"
class="ml-14"
icon="red:upload"
tooltipPosition="below"
[type]="circleButtonTypes.primary"
></iqser-circle-button>
</redaction-page-header>
@ -54,7 +54,7 @@
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'dossier-overview.no-match.title' | translate"></redaction-empty-state>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" iqserHasScrollbar>
<div
*cdkVirtualFor="let fileStatus of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.disabled]="fileStatus.excluded"
@ -172,10 +172,10 @@
</div>
</cdk-virtual-scroll-viewport>
<redaction-scroll-button [itemSize]="itemSize" [scrollViewport]="scrollViewport"></redaction-scroll-button>
<iqser-scroll-button [itemSize]="itemSize" [scrollViewport]="scrollViewport"></iqser-scroll-button>
</div>
<div [class.collapsed]="collapsedDetails" class="right-container" redactionHasScrollbar>
<div [class.collapsed]="collapsedDetails" class="right-container" iqserHasScrollbar>
<redaction-dossier-details
(openAssignDossierMembersDialog)="openAssignDossierMembersDialog()"
(openDossierDictionaryDialog)="openDossierDictionaryDialog()"

View File

@ -13,10 +13,6 @@ cdk-virtual-scroll-viewport {
grid-template-columns: auto 3fr 2fr repeat(var(--dynamic-columns, 1), 1fr) 2fr 1fr auto 11px;
.table-item {
> div {
padding-left: 10px;
}
.disabled {
color: $grey-7;
}

View File

@ -2,9 +2,9 @@
<redaction-page-header
(closeAction)="routerHistoryService.navigateToLastDossiersScreen()"
[searchPlaceholder]="'search.placeholder' | translate"
[searchPosition]="searchPositions.beforeFilters"
[searchWidth]="600"
[showCloseButton]="true"
[searchPosition]="searchPositions.beforeFilters"
></redaction-page-header>
<div class="overlay-shadow"></div>
@ -19,7 +19,7 @@
[text]="'search-screen.no-data' | translate"
></redaction-empty-state>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" iqserHasScrollbar>
<div
*cdkVirtualFor="let item of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.pointer]="true"
@ -88,11 +88,7 @@
</div>
</cdk-virtual-scroll-viewport>
<redaction-scroll-button
*ngIf="searchResult.length"
[itemSize]="itemSize"
[scrollViewport]="scrollViewport"
></redaction-scroll-button>
<iqser-scroll-button *ngIf="searchResult.length" [itemSize]="itemSize" [scrollViewport]="scrollViewport"></iqser-scroll-button>
</div>
</div>
</section>

View File

@ -14,7 +14,6 @@ export class IconsModule {
'add',
'analyse',
'approved',
'arrow-down-o',
'arrow-right',
'arrow-up',
'assign',

View File

@ -1,26 +0,0 @@
import { AfterContentChecked, Directive, ElementRef, HostBinding } from '@angular/core';
@Directive({
selector: '[redactionHasScrollbar]',
exportAs: 'redactionHasScrollbar'
})
export class HasScrollbarDirective implements AfterContentChecked {
@HostBinding('class') class = '';
constructor(private readonly _elementRef: ElementRef) {}
get hasScrollbar() {
return this._elementRef?.nativeElement.clientHeight < this._elementRef?.nativeElement.scrollHeight;
}
ngAfterContentChecked() {
this._process();
}
_process() {
const newClass = this.hasScrollbar ? 'has-scrollbar' : '';
if (this.class !== newClass) {
this.class = newClass;
}
}
}

View File

@ -10,7 +10,6 @@ import { IconsModule } from '../icons/icons.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
import { SimpleDoughnutChartComponent } from './components/simple-doughnut-chart/simple-doughnut-chart.component';
import { HasScrollbarDirective } from './directives/has-scrollbar.directive';
import { DictionaryAnnotationIconComponent } from './components/dictionary-annotation-icon/dictionary-annotation-icon.component';
import { HiddenActionComponent } from './components/hidden-action/hidden-action.component';
import { ConfirmationDialogComponent } from './dialogs/confirmation-dialog/confirmation-dialog.component';
@ -47,7 +46,7 @@ const components = [
...buttons
];
const utils = [DatePipe, HasScrollbarDirective, NavigateLastDossiersScreenDirective];
const utils = [DatePipe, NavigateLastDossiersScreenDirective];
const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, ReactiveFormsModule, CommonUiModule];

View File

@ -1,18 +0,0 @@
<svg
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.8285 12.0259L16.2427 13.4402L12 17.6828L7.7574 13.4402L9.17161 12.0259L11 13.8544V6.31724H13V13.8544L14.8285 12.0259Z"
fill="currentColor"
/>
<path
clip-rule="evenodd"
d="M19.7782 19.7782C15.4824 24.0739 8.51759 24.0739 4.22183 19.7782C-0.0739417 15.4824 -0.0739417 8.51759 4.22183 4.22183C8.51759 -0.0739419 15.4824 -0.0739419 19.7782 4.22183C24.0739 8.51759 24.0739 15.4824 19.7782 19.7782ZM18.364 18.364C14.8492 21.8787 9.15076 21.8787 5.63604 18.364C2.12132 14.8492 2.12132 9.15076 5.63604 5.63604C9.15076 2.12132 14.8492 2.12132 18.364 5.63604C21.8787 9.15076 21.8787 14.8492 18.364 18.364Z"
fill="currentColor"
fill-rule="evenodd"
/>
</svg>

Before

Width:  |  Height:  |  Size: 841 B

View File

@ -12,118 +12,6 @@
}
}
cdk-virtual-scroll-viewport {
height: calc(100vh - 50px - 31px - 111px);
overflow-y: hidden !important;
.cdk-virtual-scroll-content-wrapper {
display: grid;
.table-item {
display: contents;
> 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;
&:not(.scrollbar-placeholder):not(.selection-column) {
min-width: 110px;
}
&.selection-column {
padding-right: 0 !important;
iqser-round-checkbox .wrapper {
opacity: 0;
transition: opacity 0.2s;
&.active {
opacity: 1;
}
}
}
}
.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%, $grey-2 35%);
mat-icon {
width: 14px;
}
iqser-circle-button:not(:last-child) {
margin-right: 2px;
}
&.active {
display: flex;
// compensate for scroll
padding-right: 23px;
}
}
input,
mat-select {
margin-top: 0;
}
&:hover {
> div {
background-color: $grey-8;
&.selection-column iqser-round-checkbox .wrapper {
opacity: 1;
}
}
.action-buttons {
display: flex;
}
}
}
}
&:hover {
overflow-y: auto !important;
@include scroll-bar;
&.has-scrollbar {
.table-item {
.action-buttons {
right: 0;
padding-right: 13px;
}
.scrollbar-placeholder {
display: none;
}
}
}
}
}
.attributes-actions-container {
display: flex;
flex: 1;

@ -1 +1 @@
Subproject commit 1afafe0e28239da365d9f1e663763b210b9a9b6f
Subproject commit 1b7104ba14b1e3fae3276fd5f82d098e8a3b90c8