Merge pull request #4982 from tsiegleauq/better-mobile-tables

More mobile friendly lists
This commit is contained in:
Emanuel Schütze 2019-09-13 13:57:36 +02:00 committed by GitHub
commit 902855a6e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 281 additions and 111 deletions

View File

@ -33,7 +33,11 @@
<!-- Projector column --> <!-- Projector column -->
<div *pblNgridCellDef="'projector'; row as viewModel" class="fill ngrid-lg"> <div *pblNgridCellDef="'projector'; row as viewModel" class="fill ngrid-lg">
<os-projector-button class="projector-button" [object]="getProjectable(viewModel)" (changeEvent)="viewUpdateEvent()"></os-projector-button> <os-projector-button
class="projector-button"
[object]="getProjectable(viewModel)"
(changeEvent)="viewUpdateEvent()"
></os-projector-button>
</div> </div>
<!-- No Results --> <!-- No Results -->
@ -42,6 +46,16 @@
</div> </div>
<!-- Slot transclusion for the individual cells --> <!-- Slot transclusion for the individual cells -->
<ng-content class="ngrid-lg" select=".cell-slot"></ng-content> <div #contentWrapper>
<ng-content class="ngrid-lg" select=".cell-slot"></ng-content>
</div>
<!-- Speaker -->
<div *pblNgridCellDef="'speaker'; row as viewModel; rowContext as rowContext" class="fill">
<os-speaker-button
[object]="viewModel.contentObjectData ? viewModel.contentObjectData : viewModel"
[disabled]="multiSelect"
></os-speaker-button>
</div>
</pbl-ngrid> </pbl-ngrid>
</mat-drawer-container> </mat-drawer-container>

View File

@ -12,7 +12,7 @@ import {
} from '@angular/core'; } from '@angular/core';
import { columnFactory, createDS, PblDataSource, PblNgridComponent } from '@pebula/ngrid'; import { columnFactory, createDS, PblDataSource, PblNgridComponent } from '@pebula/ngrid';
import { PblColumnDefinition, PblNgridColumnSet } from '@pebula/ngrid/lib/table'; import { PblColumnDefinition, PblColumnFactory, PblNgridColumnSet } from '@pebula/ngrid/lib/table';
import { PblNgridDataMatrixRow } from '@pebula/ngrid/target-events'; import { PblNgridDataMatrixRow } from '@pebula/ngrid/target-events';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
@ -112,7 +112,7 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
* Current state of the multi select mode. * Current state of the multi select mode.
*/ */
@Input() @Input()
private multiSelect = false; public multiSelect = false;
/** /**
* If a Projector column should be shown (at all) * If a Projector column should be shown (at all)
@ -151,6 +151,9 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
@Input() @Input()
public columns: PblColumnDefinition[] = []; public columns: PblColumnDefinition[] = [];
/**
* Properties to filter for
*/
@Input() @Input()
public filterProps: string[]; public filterProps: string[];
@ -166,6 +169,18 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
@Input() @Input()
public showFilterBar = true; public showFilterBar = true;
/**
* If the menu should always be shown
*/
@Input()
public alwaysShowMenu = false;
/**
* If the speaker tab should appear
*/
@Input()
public showListOfSpeakers = true;
/** /**
* Inform about changes in the dataSource * Inform about changes in the dataSource
*/ */
@ -187,10 +202,15 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
*/ */
public columnSet: PblNgridColumnSet; public columnSet: PblNgridColumnSet;
/**
* To dynamically recreate the columns
*/
public columnFactory: PblColumnFactory;
/** /**
* Check if mobile and required semaphore for change detection * Check if mobile and required semaphore for change detection
*/ */
private isMobile: boolean; public isMobile: boolean;
/** /**
* Search input value * Search input value
@ -206,22 +226,38 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
/** /**
* Most, of not all list views require these * Most, of not all list views require these
*/ */
private get defaultColumns(): PblColumnDefinition[] { private get defaultStartColumns(): PblColumnDefinition[] {
const columns = [ const columns = [
{ {
prop: 'selection', prop: 'selection',
label: '', label: '',
width: this.columnMinWidth width: '40px'
} },
]; {
if (this.allowProjector && this.operator.hasPerms('core.can_manage_projector')) {
columns.push({
prop: 'projector', prop: 'projector',
label: '', label: '',
width: this.columnMinWidth width: this.columnMinWidth
}); }
} ];
return columns;
}
/**
* End columns
*/
private get defaultEndColumns(): PblColumnDefinition[] {
const columns = [
{
prop: 'speaker',
label: '',
width: '40px'
},
{
prop: 'menu',
label: '',
width: '40px'
}
];
return columns; return columns;
} }
@ -256,22 +292,42 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
public get hiddenColumns(): string[] { public get hiddenColumns(): string[] {
let hidden: string[] = []; let hidden: string[] = [];
if (this.multiSelect) { if (!this.multiSelect) {
hidden.push('projector');
} else {
hidden.push('selection'); hidden.push('selection');
} }
if (this.isMobile && this.hiddenInMobile && this.hiddenInMobile.length) { if (!this.alwaysShowMenu && !this.isMobile) {
hidden = hidden.concat(this.hiddenInMobile); hidden.push('menu');
} }
// hide the projector columns
if (
this.multiSelect ||
this.isMobile ||
!this.allowProjector ||
!this.operator.hasPerms('core.can_manage_projector')
) {
hidden.push('projector');
}
// hide the speakers in mobile
if (this.isMobile || !this.operator.hasPerms('agenda.can_see_list_of_speakers') || !this.showListOfSpeakers) {
hidden.push('speaker');
}
// hide all columns with restrictions
if (this.restricted && this.restricted.length) { if (this.restricted && this.restricted.length) {
const restrictedColumns = this.restricted const restrictedColumns = this.restricted
.filter(restriction => !this.operator.hasPerms(restriction.permission)) .filter(restriction => !this.operator.hasPerms(restriction.permission))
.map(restriction => restriction.columnName); .map(restriction => restriction.columnName);
hidden = hidden.concat(restrictedColumns); hidden = hidden.concat(restrictedColumns);
} }
// define columns that are hidden in mobile
if (this.isMobile && this.hiddenInMobile && this.hiddenInMobile.length) {
hidden = hidden.concat(this.hiddenInMobile);
}
return hidden; return hidden;
} }
@ -390,7 +446,7 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
// the constructor of this class // the constructor of this class
this.columnSet = columnFactory() this.columnSet = columnFactory()
.default({ width: this.columnMinWidth }) .default({ width: this.columnMinWidth })
.table(...this.defaultColumns, ...this.columns) .table(...this.defaultStartColumns, ...this.columns, ...this.defaultEndColumns)
.build(); .build();
// restore scroll position // restore scroll position

View File

@ -20,6 +20,7 @@
[multiSelect]="isMultiSelect" [multiSelect]="isMultiSelect"
[restricted]="restrictedColumns" [restricted]="restrictedColumns"
[hiddenInMobile]="['info']" [hiddenInMobile]="['info']"
[alwaysShowMenu]="true"
[filterProps]="filterProps" [filterProps]="filterProps"
listStorageKey="agenda" listStorageKey="agenda"
[(selectedRows)]="selectedRows" [(selectedRows)]="selectedRows"
@ -58,23 +59,11 @@
</div> </div>
</div> </div>
<!-- Speaker -->
<div *pblNgridCellDef="'speaker'; row as item; rowContext as rowContext" class="cell-slot fill">
<os-speaker-button [disabled]="isMultiSelect"></os-speaker-button>
<os-speaker-button
[object]="item.contentObjectData"
[disabled]="isMultiSelect"
(click)="saveScrollIndex('agenda', rowContext.identity)"
></os-speaker-button>
</div>
<!-- Menu --> <!-- Menu -->
<div *pblNgridCellDef="'menu'; row as item" class="cell-slot fill"> <div *pblNgridCellDef="'menu'; row as item" class="cell-slot fill">
<button <button
mat-icon-button mat-icon-button
[disabled]="isMultiSelect" [disabled]="isMultiSelect"
*osPerms="'agenda.can_manage'"
[matMenuTriggerFor]="singleItemMenu" [matMenuTriggerFor]="singleItemMenu"
(click)="$event.stopPropagation()" (click)="$event.stopPropagation()"
[matMenuTriggerData]="{ item: item }" [matMenuTriggerData]="{ item: item }"
@ -187,36 +176,45 @@
<mat-menu #singleItemMenu="matMenu"> <mat-menu #singleItemMenu="matMenu">
<ng-template matMenuContent let-item="item"> <ng-template matMenuContent let-item="item">
<!-- Done check --> <!-- Mobile entries -->
<button mat-menu-item (click)="onDoneSingleButton(item)"> <div *ngIf="vp.isMobile">
<mat-icon color="accent"> {{ item.closed ? 'check_box' : 'check_box_outline_blank' }} </mat-icon> <os-projector-button [object]="item.contentObject" [menuItem]="true"></os-projector-button>
<span translate>Done</span> <os-speaker-button [object]="item.contentObjectData" [menuItem]="true"></os-speaker-button>
</button> </div>
<!-- Edit button --> <!-- Agenda entries -->
<button mat-menu-item (click)="openEditInfo(item, $event)"> <div *osPerms="'agenda.can_manage'">
<mat-icon>edit</mat-icon> <!-- Done check -->
<span translate>Edit details</span> <button mat-menu-item (click)="onDoneSingleButton(item)">
</button> <mat-icon color="accent"> {{ item.closed ? 'check_box' : 'check_box_outline_blank' }} </mat-icon>
<span translate>Done</span>
</button>
<!-- Delete Button --> <!-- Edit button -->
<button <button mat-menu-item (click)="openEditInfo(item, $event)">
mat-menu-item <mat-icon>edit</mat-icon>
(click)="removeFromAgenda(item)" <span translate>Edit details</span>
*ngIf="item.contentObjectData.collection !== 'topics/topic'" </button>
>
<mat-icon>remove</mat-icon>
<span translate>Remove from agenda</span>
</button>
<button <!-- Delete Button -->
mat-menu-item <button
class="red-warning-text" mat-menu-item
(click)="deleteTopic(item)" (click)="removeFromAgenda(item)"
*ngIf="item.contentObjectData.collection === 'topics/topic'" *ngIf="item.contentObjectData.collection !== 'topics/topic'"
> >
<mat-icon>delete</mat-icon> <mat-icon>remove</mat-icon>
<span translate>Delete</span> <span translate>Remove from agenda</span>
</button> </button>
<button
mat-menu-item
class="red-warning-text"
(click)="deleteTopic(item)"
*ngIf="item.contentObjectData.collection === 'topics/topic'"
>
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</div>
</ng-template> </ng-template>
</mat-menu> </mat-menu>

View File

@ -80,14 +80,6 @@ export class AgendaListComponent extends BaseListViewComponent<ViewItem> impleme
{ {
prop: 'info', prop: 'info',
width: '15%' width: '15%'
},
{
prop: 'speaker',
width: this.badgeButtonWidth
},
{
prop: 'menu',
width: this.singleButtonWidth
} }
]; ];
@ -95,10 +87,6 @@ export class AgendaListComponent extends BaseListViewComponent<ViewItem> impleme
{ {
columnName: 'menu', columnName: 'menu',
permission: 'agenda.can_manage' permission: 'agenda.can_manage'
},
{
columnName: 'speaker',
permission: 'agenda.can_see_list_of_speakers'
} }
]; ];

View File

@ -58,8 +58,29 @@
</mat-chip> </mat-chip>
</mat-chip-list> </mat-chip-list>
</div> </div>
<!-- Menu -->
<div *pblNgridCellDef="'menu'; row as assignment" class="cell-slot fill">
<button
mat-icon-button
[disabled]="isMultiSelect"
[matMenuTriggerFor]="singleItemMenu"
(click)="$event.stopPropagation()"
[matMenuTriggerData]="{ assignment: assignment }"
>
<mat-icon>more_vert</mat-icon>
</button>
</div>
</os-list-view-table> </os-list-view-table>
<!-- Menu for mobile entries -->
<mat-menu #singleItemMenu="matMenu">
<ng-template matMenuContent let-assignment="assignment">
<os-projector-button [object]="assignment" [menuItem]="true"></os-projector-button>
<os-speaker-button [object]="assignment" [menuItem]="true"></os-speaker-button>
</ng-template>
</mat-menu>
<mat-menu #assignmentMenu="matMenu"> <mat-menu #assignmentMenu="matMenu">
<div *ngIf="!isMultiSelect"> <div *ngIf="!isMultiSelect">
<button mat-menu-item *osPerms="'assignment.can_manage'" (click)="toggleMultiSelect()"> <button mat-menu-item *osPerms="'assignment.can_manage'" (click)="toggleMultiSelect()">

View File

@ -88,12 +88,28 @@
</div> </div>
</div> </div>
<!-- List of Speakers --> <!-- Menu -->
<div *pblNgridCellDef="'speakers'; row as motion" class="cell-slot fill"> <div *pblNgridCellDef="'menu'; row as motion" class="cell-slot fill">
<os-speaker-button [object]="motion"></os-speaker-button> <button
mat-icon-button
[disabled]="isMultiSelect"
[matMenuTriggerFor]="singleItemMenu"
(click)="$event.stopPropagation()"
[matMenuTriggerData]="{ motion: motion }"
>
<mat-icon>more_vert</mat-icon>
</button>
</div> </div>
</os-list-view-table> </os-list-view-table>
<!-- Menu for mobile entries -->
<mat-menu #singleItemMenu="matMenu">
<ng-template matMenuContent let-motion="motion">
<os-projector-button [object]="motion" [menuItem]="true"></os-projector-button>
<os-speaker-button [object]="motion" [menuItem]="true"></os-speaker-button>
</ng-template>
</mat-menu>
<mat-menu #amendmentListMenu="matMenu"> <mat-menu #amendmentListMenu="matMenu">
<div *ngIf="!isMultiSelect"> <div *ngIf="!isMultiSelect">
<div *osPerms="'motions.can_manage'"> <div *osPerms="'motions.can_manage'">

View File

@ -15,9 +15,11 @@
<os-list-view-table <os-list-view-table
[repo]="repo" [repo]="repo"
[allowProjector]="false" [allowProjector]="false"
[showListOfSpeakers]="false"
[columns]="tableColumnDefinition" [columns]="tableColumnDefinition"
[filterProps]="filterProps" [filterProps]="filterProps"
listStorageKey="category" listStorageKey="category"
[hiddenInMobile]="['menu']"
(dataSourceChange)="onDataSourceChange($event)" (dataSourceChange)="onDataSourceChange($event)"
> >
<!-- Title --> <!-- Title -->

View File

@ -35,6 +35,7 @@
[columns]="tableColumnDefinition" [columns]="tableColumnDefinition"
[restricted]="restrictedColumns" [restricted]="restrictedColumns"
[filterProps]="filterProps" [filterProps]="filterProps"
[hiddenInMobile]="['remove']"
(dataSourceChange)="onDataSourceChange($event)" (dataSourceChange)="onDataSourceChange($event)"
> >
<!-- Title column --> <!-- Title column -->
@ -71,8 +72,33 @@
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</div> </div>
<!-- Menu -->
<div *pblNgridCellDef="'menu'; row as motion" class="cell-slot fill">
<button
mat-icon-button
[disabled]="isMultiSelect"
[matMenuTriggerFor]="singleItemMenu"
(click)="$event.stopPropagation()"
[matMenuTriggerData]="{ motion: motion }"
>
<mat-icon>more_vert</mat-icon>
</button>
</div>
</os-list-view-table> </os-list-view-table>
<!-- Menu for mobile entries -->
<mat-menu #singleItemMenu="matMenu">
<ng-template matMenuContent let-motion="motion">
<os-projector-button [object]="motion" [menuItem]="true"></os-projector-button>
<os-speaker-button [object]="motion" [menuItem]="true"></os-speaker-button>
<button mat-menu-item class="red-warning-text" (click)="onRemoveMotionButton(motion)">
<mat-icon>close</mat-icon>
<span translate>Remove from motion block</span>
</button>
</ng-template>
</mat-menu>
<!-- The menu content --> <!-- The menu content -->
<mat-menu #motionBlockMenu="matMenu"> <mat-menu #motionBlockMenu="matMenu">
<div *ngIf="vp.isMobile"> <div *ngIf="vp.isMobile">

View File

@ -34,16 +34,28 @@
<span class="os-amount-chip" matTooltip="{{ 'Motions' | translate }}">{{ block.motions.length }}</span> <span class="os-amount-chip" matTooltip="{{ 'Motions' | translate }}">{{ block.motions.length }}</span>
</div> </div>
<!-- Speaker column--> <!-- Menu -->
<div *pblNgridCellDef="'speaker'; row as block; rowContext as rowContext" class="fill"> <div *pblNgridCellDef="'menu'; row as block" class="cell-slot fill">
<os-speaker-button <button
[object]="block" mat-icon-button
[disabled]="isMultiSelect" [disabled]="isMultiSelect"
(click)="saveScrollIndex('motionBlock', rowContext.identity)" [matMenuTriggerFor]="singleItemMenu"
></os-speaker-button> (click)="$event.stopPropagation()"
[matMenuTriggerData]="{ block: block }"
>
<mat-icon>more_vert</mat-icon>
</button>
</div> </div>
</os-list-view-table> </os-list-view-table>
<!-- Menu for mobile entries -->
<mat-menu #singleItemMenu="matMenu">
<ng-template matMenuContent let-block="block">
<os-projector-button [object]="block" [menuItem]="true"></os-projector-button>
<os-speaker-button [object]="block" [menuItem]="true"></os-speaker-button>
</ng-template>
</mat-menu>
<!-- Template for new motion block dialog --> <!-- Template for new motion block dialog -->
<ng-template #newMotionBlockDialog> <ng-template #newMotionBlockDialog>
<h1 mat-dialog-title> <h1 mat-dialog-title>

View File

@ -72,10 +72,6 @@ export class MotionBlockListComponent extends BaseListViewComponent<ViewMotionBl
{ {
prop: 'amount', prop: 'amount',
label: this.translate.instant('Motions') label: this.translate.instant('Motions')
},
{
prop: 'speaker',
width: this.badgeButtonWidth
} }
]; ];

View File

@ -47,9 +47,8 @@
[sortService]="sortService" [sortService]="sortService"
[columns]="tableColumnDefinition" [columns]="tableColumnDefinition"
[multiSelect]="isMultiSelect" [multiSelect]="isMultiSelect"
[restricted]="restrictedColumns"
[filterProps]="filterProps" [filterProps]="filterProps"
[hiddenInMobile]="['state']" [hiddenInMobile]="['identifier', 'state']"
listStorageKey="motion" listStorageKey="motion"
[(selectedRows)]="selectedRows" [(selectedRows)]="selectedRows"
(dataSourceChange)="onDataSourceChange($event)" (dataSourceChange)="onDataSourceChange($event)"
@ -84,6 +83,10 @@
<!-- The title --> <!-- The title -->
<span class="motion-list-title"> <span class="motion-list-title">
<span *ngIf="vp.isMobile && motion.identifier">
{{ motion.identifier }}
<span>&middot;</span>
</span>
{{ motion.title }} {{ motion.title }}
</span> </span>
</div> </div>
@ -156,17 +159,29 @@
</div> </div>
</div> </div>
<!-- Speaker column--> <!-- Menu -->
<div *pblNgridCellDef="'speaker'; row as motion; rowContext as rowContext" class="fill"> <div *pblNgridCellDef="'menu'; row as motion" class="cell-slot fill">
<os-speaker-button <button
[object]="motion" mat-icon-button
[disabled]="isMultiSelect" [disabled]="isMultiSelect"
(click)="saveScrollIndex('motion', rowContext.identity)" [matMenuTriggerFor]="singleItemMenu"
></os-speaker-button> (click)="$event.stopPropagation()"
[matMenuTriggerData]="{ motion: motion }"
>
<mat-icon>more_vert</mat-icon>
</button>
</div> </div>
</os-list-view-table> </os-list-view-table>
</ng-template> </ng-template>
<!-- Menu for mobile entries -->
<mat-menu #singleItemMenu="matMenu">
<ng-template matMenuContent let-motion="motion">
<os-projector-button [object]="motion" [menuItem]="true"></os-projector-button>
<os-speaker-button [object]="motion" [menuItem]="true"></os-speaker-button>
</ng-template>
</mat-menu>
<ng-template #tiles> <ng-template #tiles>
<os-grid-layout> <os-grid-layout>
<os-block-tile <os-block-tile

View File

@ -16,7 +16,7 @@ import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.
import { OsFilterOptionCondition } from 'app/core/ui-services/base-filter-list.service'; import { OsFilterOptionCondition } from 'app/core/ui-services/base-filter-list.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { OverlayService } from 'app/core/ui-services/overlay.service'; import { OverlayService } from 'app/core/ui-services/overlay.service';
import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component'; import { ViewportService } from 'app/core/ui-services/viewport.service';
import { infoDialogSettings, largeDialogSettings } from 'app/shared/utils/dialog-settings'; import { infoDialogSettings, largeDialogSettings } from 'app/shared/utils/dialog-settings';
import { BaseListViewComponent } from 'app/site/base/base-list-view'; import { BaseListViewComponent } from 'app/site/base/base-list-view';
import { ViewCategory } from 'app/site/motions/models/view-category'; import { ViewCategory } from 'app/site/motions/models/view-category';
@ -114,10 +114,6 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
prop: 'state', prop: 'state',
width: '20%', width: '20%',
minWidth: 160 minWidth: 160
},
{
prop: 'speaker',
width: this.badgeButtonWidth
} }
]; ];
@ -139,16 +135,6 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
public categories: ViewCategory[] = []; public categories: ViewCategory[] = [];
public motionBlocks: ViewMotionBlock[] = []; public motionBlocks: ViewMotionBlock[] = [];
/**
* Columns that demand certain permissions
*/
public restrictedColumns: ColumnRestriction[] = [
{
columnName: 'speaker',
permission: 'agenda.can_see_list_of_speakers'
}
];
/** /**
* Define extra filter properties * Define extra filter properties
* *
@ -214,7 +200,8 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
public multiselectService: MotionMultiselectService, public multiselectService: MotionMultiselectService,
public perms: LocalPermissionsService, public perms: LocalPermissionsService,
private motionExport: MotionExportService, private motionExport: MotionExportService,
private overlayService: OverlayService private overlayService: OverlayService,
public vp: ViewportService
) { ) {
super(titleService, translate, matSnackBar, storage); super(titleService, translate, matSnackBar, storage);
this.canMultiSelect = true; this.canMultiSelect = true;

View File

@ -7,6 +7,10 @@
[repo]="workflowRepo" [repo]="workflowRepo"
[columns]="tableColumnDefinition" [columns]="tableColumnDefinition"
listStorageKey="workflow" listStorageKey="workflow"
[allowProjector]="false"
[hiddenInMobile]="['menu']"
[showListOfSpeakers]="false"
[filterProps]="filterProps"
(dataSourceChange)="onDataSourceChange($event)" (dataSourceChange)="onDataSourceChange($event)"
> >
<!-- Name column --> <!-- Name column -->

View File

@ -41,6 +41,11 @@ export class WorkflowListComponent extends BaseListViewComponent<ViewWorkflow> i
} }
]; ];
/**
* Define extra filter properties
*/
public filterProps = ['name', 'states'];
/** /**
* Constructor * Constructor
* *

View File

@ -6,6 +6,8 @@
[repo]="repo" [repo]="repo"
[columns]="tableColumnDefinition" [columns]="tableColumnDefinition"
[allowProjector]="false" [allowProjector]="false"
[showListOfSpeakers]="false"
[hiddenInMobile]="['menu']"
[(selectedRows)]="selectedRows" [(selectedRows)]="selectedRows"
(dataSourceChange)="onDataSourceChange($event)" (dataSourceChange)="onDataSourceChange($event)"
[filterProps]="['name']" [filterProps]="['name']"

View File

@ -20,8 +20,9 @@
[sortService]="sortService" [sortService]="sortService"
[columns]="tableColumnDefinition" [columns]="tableColumnDefinition"
[filterProps]="filterProps" [filterProps]="filterProps"
[showListOfSpeakers]="false"
[multiSelect]="isMultiSelect" [multiSelect]="isMultiSelect"
[hiddenInMobile]="['group']" [hiddenInMobile]="['group', 'presence']"
listStorageKey="user" listStorageKey="user"
[(selectedRows)]="selectedRows" [(selectedRows)]="selectedRows"
(dataSourceChange)="onDataSourceChange($event)" (dataSourceChange)="onDataSourceChange($event)"
@ -101,8 +102,34 @@
<span translate>Present</span> <span translate>Present</span>
</mat-checkbox> </mat-checkbox>
</div> </div>
<!-- Menu -->
<div *pblNgridCellDef="'menu'; row as user" class="cell-slot fill">
<button
mat-icon-button
[disabled]="isMultiSelect"
[matMenuTriggerFor]="singleItemMenu"
(click)="$event.stopPropagation()"
[matMenuTriggerData]="{ user: user }"
>
<mat-icon>more_vert</mat-icon>
</button>
</div>
</os-list-view-table> </os-list-view-table>
<!-- Menu for mobile entries -->
<mat-menu #singleItemMenu="matMenu">
<ng-template matMenuContent let-user="user">
<os-projector-button [object]="user" [menuItem]="true"></os-projector-button>
<!-- Presence -->
<button mat-menu-item (click)="setPresent(user)">
<mat-icon color="accent"> {{ user.is_present ? 'check_box' : 'check_box_outline_blank' }} </mat-icon>
<span translate>Present</span>
</button>
</ng-template>
</mat-menu>
<mat-menu #userMenu="matMenu"> <mat-menu #userMenu="matMenu">
<div *ngIf="!isMultiSelect"> <div *ngIf="!isMultiSelect">
<button mat-menu-item *osPerms="'users.can_manage'" (click)="toggleMultiSelect()"> <button mat-menu-item *osPerms="'users.can_manage'" (click)="toggleMultiSelect()">

View File

@ -120,7 +120,8 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
width: '15%' width: '15%'
}, },
{ {
prop: 'infos' prop: 'infos',
width: this.singleButtonWidth
}, },
{ {
prop: 'presence', prop: 'presence',