Merge pull request #4472 from tsiegleauq/countdown-control-component
Add Countdowns in projector detail
This commit is contained in:
commit
5fbf682754
@ -1,5 +1,9 @@
|
||||
<div id="countdown" [ngClass]="{
|
||||
'negative': seconds <= 0,
|
||||
'warning_time': seconds <= warningTime && seconds > 0 }">
|
||||
<div
|
||||
id="countdown"
|
||||
[ngClass]="{
|
||||
negative: seconds <= 0,
|
||||
warning_time: seconds <= warningTime && seconds > 0
|
||||
}"
|
||||
>
|
||||
{{ time }}
|
||||
</div>
|
||||
|
@ -10,9 +10,4 @@
|
||||
color: mat-color($contrast, A200) !important;
|
||||
background-color: mat-color($contrast, 500) !important;
|
||||
}
|
||||
|
||||
.mat-menu-item.projector-active,
|
||||
.projector-active > .mat-icon {
|
||||
color: mat-color($primary) !important;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
isProjectable,
|
||||
isProjectorElementBuildDeskriptor
|
||||
} from 'app/site/base/projectable';
|
||||
import { Projector } from 'app/shared/models/core/projector';
|
||||
import { ProjectorService } from 'app/core/core-services/projector.service';
|
||||
import { ProjectionDialogService } from 'app/core/ui-services/projection-dialog.service';
|
||||
|
||||
@ -45,6 +46,12 @@ export class ProjectorButtonComponent implements OnInit {
|
||||
@Input()
|
||||
public menuItem = false;
|
||||
|
||||
/**
|
||||
* Pre-define projection target
|
||||
*/
|
||||
@Input()
|
||||
public projector: Projector;
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
*/
|
||||
@ -68,9 +75,21 @@ export class ProjectorButtonComponent implements OnInit {
|
||||
event.stopPropagation();
|
||||
}
|
||||
if (this.object) {
|
||||
if (this.projector) {
|
||||
// if the projection target was defines before
|
||||
if (this.isProjected()) {
|
||||
// remove the projected object
|
||||
this.projectorService.removeFrom(this.projector, this.object);
|
||||
} else {
|
||||
// instantly project the object
|
||||
this.projectorService.projectOn(this.projector, this.object);
|
||||
}
|
||||
} else {
|
||||
// open the projection dialog
|
||||
this.projectionDialogService.openProjectDialogFor(this.object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -1,12 +1,71 @@
|
||||
<div *ngIf="countdown">
|
||||
<os-countdown-time [countdown]="countdown" [warningTime]="warningTime"></os-countdown-time>
|
||||
<button type="button" [disabled]="!canStop()" mat-icon-button (click)="stop($event)">
|
||||
<mat-card *ngIf="countdown">
|
||||
<div class="grid-wrapper">
|
||||
<div class="projector-button">
|
||||
<os-projector-button [object]="countdown" [projector]="projector"></os-projector-button>
|
||||
</div>
|
||||
|
||||
<div class="title">{{ countdown.getTitle() | translate }}</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="small-mat-icon-button"
|
||||
mat-icon-button
|
||||
(click)="onBringDialog()"
|
||||
matTooltip="{{ 'Open projection dialog' | translate }}"
|
||||
>
|
||||
<mat-icon>
|
||||
open_in_new
|
||||
</mat-icon>
|
||||
</button>
|
||||
<button type="button" class="small-mat-icon-button" mat-icon-button (click)="onEdit()">
|
||||
<mat-icon>
|
||||
edit
|
||||
</mat-icon>
|
||||
</button>
|
||||
<button type="button" class="small-mat-icon-button" mat-icon-button (click)="onDelete()">
|
||||
<mat-icon>
|
||||
close
|
||||
</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="timer">
|
||||
<os-countdown-time
|
||||
class="larger-countdown"
|
||||
[countdown]="countdown"
|
||||
[warningTime]="warningTime"
|
||||
></os-countdown-time>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="small-mat-icon-button"
|
||||
mat-icon-button
|
||||
[disabled]="!canStop()"
|
||||
(click)="stop($event)"
|
||||
>
|
||||
<mat-icon>stop</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="!countdown.running" type="button" mat-icon-button (click)="start($event)">
|
||||
<button
|
||||
type="button"
|
||||
class="small-mat-icon-button"
|
||||
mat-icon-button
|
||||
(click)="start($event)"
|
||||
*ngIf="!countdown.running"
|
||||
>
|
||||
<mat-icon>play_arrow</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="countdown.running" type="button" mat-icon-button (click)="pause($event)">
|
||||
<button
|
||||
type="button"
|
||||
class="small-mat-icon-button"
|
||||
mat-icon-button
|
||||
(click)="pause($event)"
|
||||
*ngIf="countdown.running"
|
||||
>
|
||||
<mat-icon>pause</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
||||
|
@ -0,0 +1,62 @@
|
||||
.grid-wrapper {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-areas:
|
||||
'project title buttons'
|
||||
'project controls controls';
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: min-content auto auto;
|
||||
}
|
||||
|
||||
.projector-button {
|
||||
grid-area: project;
|
||||
margin: auto 10px auto 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-area: title;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
grid-area: buttons;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.timer {
|
||||
grid-area: controls;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.larger-countdown {
|
||||
font-size: 150%;
|
||||
min-width: 60px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
// smaller and closer mat icon buttons.
|
||||
// use on button-tags
|
||||
// TODO: Somehow does not apply when in style.scss
|
||||
.small-mat-icon-button {
|
||||
$size: 20px;
|
||||
margin: 0 5px;
|
||||
position: relative;
|
||||
width: $size;
|
||||
height: $size;
|
||||
|
||||
::ng-deep .mat-icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
line-height: normal;
|
||||
width: $size;
|
||||
height: $size;
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-size: $size;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
|
||||
@ -8,26 +8,60 @@ import { BaseViewComponent } from '../../../base/base-view';
|
||||
import { ViewCountdown } from '../../models/view-countdown';
|
||||
import { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { Projector } from 'app/shared/models/core/projector';
|
||||
import { ProjectionDialogService } from 'app/core/ui-services/projection-dialog.service';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Component({
|
||||
selector: 'os-countdown-controls',
|
||||
templateUrl: './countdown-controls.component.html'
|
||||
templateUrl: './countdown-controls.component.html',
|
||||
styleUrls: ['./countdown-controls.component.scss']
|
||||
})
|
||||
export class CountdownControlsComponent extends BaseViewComponent {
|
||||
/**
|
||||
* Countdown as input
|
||||
*/
|
||||
@Input()
|
||||
public countdown: ViewCountdown;
|
||||
|
||||
/**
|
||||
* Edit event
|
||||
*/
|
||||
@Output()
|
||||
public editEvent = new EventEmitter<ViewCountdown>();
|
||||
|
||||
/**
|
||||
* Pre defined projection target (if any)
|
||||
*/
|
||||
@Input()
|
||||
public projector: Projector;
|
||||
|
||||
/**
|
||||
* The time in seconds to make the countdown orange, is the countdown is below this value.
|
||||
*/
|
||||
public warningTime: number;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param titleService
|
||||
* @param translate
|
||||
* @param matSnackBar
|
||||
* @param repo
|
||||
* @param configService
|
||||
* @param promptService
|
||||
*/
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
private repo: CountdownRepositoryService,
|
||||
private configService: ConfigService
|
||||
private configService: ConfigService,
|
||||
private promptService: PromptService,
|
||||
private projectionDialogService: ProjectionDialogService
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
|
||||
@ -64,4 +98,29 @@ export class CountdownControlsComponent extends BaseViewComponent {
|
||||
public canStop(): boolean {
|
||||
return this.countdown.running || this.countdown.countdown_time !== this.countdown.default_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires an edit event
|
||||
*/
|
||||
public onEdit(): void {
|
||||
this.editEvent.next(this.countdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Brings the projection dialog
|
||||
*/
|
||||
public onBringDialog(): void {
|
||||
this.projectionDialogService.openProjectDialogFor(this.countdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* On delete button
|
||||
*/
|
||||
public async onDelete(): Promise<void> {
|
||||
const content =
|
||||
this.translate.instant('Delete countdown') + ` ${this.translate.instant(this.countdown.title)}?`;
|
||||
if (await this.promptService.open('Are you sure?', content)) {
|
||||
this.repo.delete(this.countdown).then(() => {}, this.raiseError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
<h1 mat-dialog-title>
|
||||
<span translate>Countdown</span>
|
||||
</h1>
|
||||
|
||||
<form [formGroup]="countdownForm">
|
||||
<div mat-dialog-content>
|
||||
<!-- Title field -->
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
osAutofocus
|
||||
placeholder="{{ 'Countdown title' | translate }}"
|
||||
formControlName="title"
|
||||
required
|
||||
/>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Description field -->
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{ 'Countdown description' | translate }}" formControlName="description" />
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Time field -->
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{ 'Countdown time' | translate }}" formControlName="duration" required />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<!-- Submit countdown button -->
|
||||
<button
|
||||
type="submit"
|
||||
mat-button
|
||||
color="primary"
|
||||
[mat-dialog-close]="countdownForm.value"
|
||||
[disabled]="!countdownForm.valid"
|
||||
>
|
||||
<span translate>Save</span>
|
||||
</button>
|
||||
|
||||
<!-- Cancel Countdown button -->
|
||||
<button type="button" mat-button [mat-dialog-close]="null">
|
||||
<span translate>Cancel</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
@ -0,0 +1,3 @@
|
||||
.mat-form-field {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CountdownDialogComponent, CountdownData } from './countdown-dialog.component';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
|
||||
describe('CountdownDialogComponent', () => {
|
||||
let component: CountdownDialogComponent;
|
||||
let fixture: ComponentFixture<CountdownDialogComponent>;
|
||||
|
||||
const dialogData: CountdownData = {
|
||||
title: '',
|
||||
description: '',
|
||||
duration: ''
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
declarations: [CountdownDialogComponent],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: {} },
|
||||
{
|
||||
provide: MAT_DIALOG_DATA,
|
||||
useValue: dialogData
|
||||
}
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CountdownDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,79 @@
|
||||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatSnackBar, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
|
||||
/**
|
||||
* Countdown data for the form
|
||||
*/
|
||||
export interface CountdownData {
|
||||
title: string;
|
||||
description: string;
|
||||
duration: string;
|
||||
count?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog component for countdowns
|
||||
*/
|
||||
@Component({
|
||||
selector: 'os-countdown-dialog',
|
||||
templateUrl: './countdown-dialog.component.html',
|
||||
styleUrls: ['./countdown-dialog.component.scss']
|
||||
})
|
||||
export class CountdownDialogComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* The form data
|
||||
*/
|
||||
public countdownForm: FormGroup;
|
||||
|
||||
/**
|
||||
* Holds the default time which was set in the config
|
||||
*/
|
||||
private defaultTime: number;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param title Title service. Required by parent
|
||||
* @param matSnackBar Required by parent
|
||||
* @param configService Read out config variables
|
||||
* @param translate Required by parent
|
||||
* @param formBuilder To build the form
|
||||
* @param durationService Converts duration numbers to string
|
||||
* @param data The mat dialog data, contains the values to display (if any)
|
||||
*/
|
||||
public constructor(
|
||||
title: Title,
|
||||
matSnackBar: MatSnackBar,
|
||||
configService: ConfigService,
|
||||
translate: TranslateService,
|
||||
private formBuilder: FormBuilder,
|
||||
private durationService: DurationService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: CountdownData
|
||||
) {
|
||||
super(title, translate, matSnackBar);
|
||||
this.defaultTime = configService.instant<number>('projector_default_countdown');
|
||||
}
|
||||
|
||||
/**
|
||||
* Init. Creates the form
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
const time = this.data.duration || this.durationService.durationToString(this.defaultTime, 'm');
|
||||
const title = this.data.title || `${this.translate.instant('Countdown')} ${this.data.count + 1}`;
|
||||
|
||||
this.countdownForm = this.formBuilder.group({
|
||||
title: [title, Validators.required],
|
||||
description: [this.data.description],
|
||||
// TODO: custom form validation. This needs to be a valid minute duration
|
||||
duration: [time, Validators.required]
|
||||
});
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
<os-head-bar [nav]="false" [goBack]="true" [mainButton]="true" (mainEvent)="onPlusButton()">
|
||||
<!-- Title -->
|
||||
<div class="title-slot">
|
||||
<h2 translate>Countdowns</h2>
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<div class="head-spacer"></div>
|
||||
<mat-card *ngIf="countdownToCreate">
|
||||
<mat-card-title translate>New countdown</mat-card-title>
|
||||
<mat-card-content>
|
||||
<form [formGroup]="createForm" (keydown)="onKeyDownCreate($event)">
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="title" matInput placeholder="{{ 'Title' | translate }}" required />
|
||||
<mat-hint *ngIf="!createForm.controls.title.valid">
|
||||
<span translate>Required</span>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="description" matInput placeholder="{{ 'Description' | translate }}" />
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="default_time" matInput placeholder="{{ 'Time' | translate }}" required />
|
||||
<mat-hint *ngIf="!createForm.controls.default_time.valid">
|
||||
<span translate>Required</span>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-button (click)="create()">
|
||||
<span translate>Save</span>
|
||||
</button>
|
||||
<button mat-button (click)="onCancelCreate()">
|
||||
<span translate>Cancel</span>
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
||||
<mat-accordion class="os-card">
|
||||
<mat-expansion-panel
|
||||
*ngFor="let countdown of countdowns"
|
||||
(opened)="openId = countdown.id"
|
||||
(closed)="panelClosed(countdown)"
|
||||
[expanded]="openId === countdown.id"
|
||||
multiple="false"
|
||||
>
|
||||
<!-- Projector button and countdown description-->
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div class="header-container">
|
||||
<div class="header-projector-button">
|
||||
<os-projector-button [object]="countdown"></os-projector-button>
|
||||
</div>
|
||||
<div class="header-name">
|
||||
{{ countdown.getTitle() | translate }}
|
||||
</div>
|
||||
<div class="header-controls">
|
||||
<os-countdown-controls [countdown]="countdown"></os-countdown-controls>
|
||||
</div>
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<form [formGroup]="updateForm" *ngIf="editId === countdown.id" (keydown)="onKeyDownUpdate($event)">
|
||||
<h5 translate>Edit countdown</h5>
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="title" matInput placeholder="{{ 'Title' | translate }}" required />
|
||||
<mat-hint *ngIf="!updateForm.controls.title.valid">
|
||||
<span translate>Required</span>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input
|
||||
formControlName="description"
|
||||
matInput
|
||||
placeholder="{{ 'Description' | translate }}"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="default_time" matInput placeholder="{{ 'Time' | translate }}" required />
|
||||
<mat-hint *ngIf="!updateForm.controls.default_time.valid">
|
||||
<span translate>Required</span>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
</form>
|
||||
<mat-action-row>
|
||||
<button
|
||||
*ngIf="editId !== countdown.id"
|
||||
mat-button
|
||||
class="on-transition-fade"
|
||||
(click)="onEditButton(countdown)"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="editId === countdown.id"
|
||||
mat-button
|
||||
class="on-transition-fade"
|
||||
(click)="onCancelUpdate()"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="editId === countdown.id"
|
||||
mat-button
|
||||
class="on-transition-fade"
|
||||
(click)="onSaveButton(countdown)"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon>save</mat-icon>
|
||||
</button>
|
||||
<button mat-button class="on-transition-fade" (click)="onDeleteButton(countdown)" mat-icon-button>
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</mat-action-row>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
||||
<mat-card *ngIf="countdowns.length === 0">
|
||||
<mat-card-content>
|
||||
<div class="no-content" translate>No countdowns</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
@ -1,41 +0,0 @@
|
||||
.head-spacer {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
text-align: right;
|
||||
background: white; /* TODO: remove this and replace with theme */
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
mat-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: grid;
|
||||
grid-template-rows: auto;
|
||||
grid-template-columns: 40px 1fr 2fr;
|
||||
width: 100%;
|
||||
|
||||
> div {
|
||||
grid-row-start: 1;
|
||||
grid-row-end: span 1;
|
||||
grid-column-end: span 2;
|
||||
}
|
||||
|
||||
.header-projector-button {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 2;
|
||||
}
|
||||
|
||||
.header-name {
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 3;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
grid-column-start: 3;
|
||||
grid-column-end: 4;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CountdownListComponent } from './countdown-list.component';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
import { CountdownControlsComponent } from '../countdown-controls/countdown-controls.component';
|
||||
|
||||
describe('CountdownListComponent', () => {
|
||||
let component: CountdownListComponent;
|
||||
let fixture: ComponentFixture<CountdownListComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
declarations: [CountdownListComponent, CountdownControlsComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CountdownListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,218 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { BaseViewComponent } from '../../../base/base-view';
|
||||
import { ViewCountdown } from '../../models/view-countdown';
|
||||
import { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
|
||||
import { Countdown } from 'app/shared/models/core/countdown';
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
|
||||
/**
|
||||
* List view for countdowns.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'os-countdown-list',
|
||||
templateUrl: './countdown-list.component.html',
|
||||
styleUrls: ['./countdown-list.component.scss']
|
||||
})
|
||||
export class CountdownListComponent extends BaseViewComponent implements OnInit {
|
||||
public countdownToCreate: Countdown | null;
|
||||
|
||||
/**
|
||||
* Source of the Data
|
||||
*/
|
||||
public countdowns: ViewCountdown[] = [];
|
||||
|
||||
/**
|
||||
* The current focussed formgroup
|
||||
*/
|
||||
public updateForm: FormGroup;
|
||||
|
||||
public createForm: FormGroup;
|
||||
|
||||
public openId: number | null;
|
||||
public editId: number | null;
|
||||
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
protected translate: TranslateService, // protected required for ng-translate-extract
|
||||
matSnackBar: MatSnackBar,
|
||||
private repo: CountdownRepositoryService,
|
||||
private formBuilder: FormBuilder,
|
||||
private promptService: PromptService,
|
||||
private durationService: DurationService
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
|
||||
const form = {
|
||||
description: [''],
|
||||
default_time: ['', Validators.required],
|
||||
title: ['', Validators.required]
|
||||
};
|
||||
this.createForm = this.formBuilder.group(form);
|
||||
this.updateForm = this.formBuilder.group(form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init function.
|
||||
*
|
||||
* Sets the title and gets/observes countdowns from DataStore
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
super.setTitle('Countdowns');
|
||||
this.repo.getViewModelListObservable().subscribe(newCountdowns => {
|
||||
this.countdowns = newCountdowns;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new countdown.
|
||||
*/
|
||||
public onPlusButton(): void {
|
||||
if (!this.countdownToCreate) {
|
||||
this.createForm.reset();
|
||||
this.createForm.setValue({
|
||||
description: '',
|
||||
title: '',
|
||||
default_time: '1:00 m'
|
||||
});
|
||||
this.countdownToCreate = new Countdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler when clicking on create to create a new statute paragraph
|
||||
*/
|
||||
public create(): void {
|
||||
if (this.createForm.valid) {
|
||||
let default_time = this.durationService.stringToDuration(this.createForm.value.default_time, 'm');
|
||||
if (default_time === 0) {
|
||||
default_time = 60;
|
||||
}
|
||||
|
||||
const newValues: Partial<Countdown> = {
|
||||
description: this.createForm.value.description,
|
||||
title: this.createForm.value.title,
|
||||
default_time: default_time
|
||||
};
|
||||
newValues.countdown_time = default_time;
|
||||
this.countdownToCreate.patchValues(newValues);
|
||||
this.repo.create(this.countdownToCreate).then(() => {
|
||||
this.countdownToCreate = null;
|
||||
}, this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed on edit button
|
||||
* @param countdown
|
||||
*/
|
||||
public onEditButton(countdown: ViewCountdown): void {
|
||||
this.editId = countdown.id;
|
||||
|
||||
this.updateForm.setValue({
|
||||
description: countdown.description,
|
||||
title: this.translate.instant(countdown.title),
|
||||
default_time: this.durationService.durationToString(countdown.default_time, 'm')
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the countdown
|
||||
* @param countdown The countdown to save
|
||||
*/
|
||||
public onSaveButton(countdown: ViewCountdown): void {
|
||||
if (this.updateForm.valid) {
|
||||
let default_time = this.durationService.stringToDuration(this.updateForm.value.default_time, 'm');
|
||||
if (default_time === 0) {
|
||||
default_time = 60;
|
||||
}
|
||||
const newValues: Partial<Countdown> = {
|
||||
title: this.updateForm.value.title,
|
||||
description: this.updateForm.value.description,
|
||||
default_time: default_time
|
||||
};
|
||||
if (!countdown.running) {
|
||||
newValues.countdown_time = default_time;
|
||||
}
|
||||
this.repo.update(newValues, countdown).then(() => {
|
||||
this.openId = this.editId = null;
|
||||
}, this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is executed, when the delete button is pressed
|
||||
*
|
||||
* @param countdown The countdown to delete
|
||||
*/
|
||||
public async onDeleteButton(countdown: ViewCountdown): Promise<void> {
|
||||
const title = this.translate.instant('Are you sure you want to delete this countdown?');
|
||||
const content = countdown.title;
|
||||
if (await this.promptService.open(title, content)) {
|
||||
this.repo.delete(countdown).then(() => (this.openId = this.editId = null), this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is executed when a mat-extension-panel is closed
|
||||
*
|
||||
* @param countdown the statute paragraph in the panel
|
||||
*/
|
||||
public panelClosed(countdown: ViewCountdown): void {
|
||||
this.openId = null;
|
||||
if (this.editId) {
|
||||
this.onSaveButton(countdown);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clicking Shift and Enter will save automatically
|
||||
* clicking Escape will cancel the process
|
||||
*
|
||||
* @param event has the code
|
||||
*/
|
||||
public onKeyDownCreate(event: KeyboardEvent): void {
|
||||
if (event.key === 'Enter' && event.shiftKey) {
|
||||
this.create();
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
this.onCancelCreate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the current form action
|
||||
*/
|
||||
public onCancelCreate(): void {
|
||||
this.countdownToCreate = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* clicking Shift and Enter will save automatically
|
||||
* clicking Escape will cancel the process
|
||||
*
|
||||
* @param event has the code
|
||||
*/
|
||||
public onKeyDownUpdate(event: KeyboardEvent): void {
|
||||
if (event.key === 'Enter' && event.shiftKey) {
|
||||
const countdown = this.countdowns.find(x => x.id === this.editId);
|
||||
this.onSaveButton(countdown);
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
this.onCancelUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the current form action
|
||||
*/
|
||||
public onCancelUpdate(): void {
|
||||
this.editId = null;
|
||||
}
|
||||
}
|
@ -180,24 +180,28 @@
|
||||
</mat-expansion-panel>
|
||||
|
||||
<!-- countdowns -->
|
||||
<mat-expansion-panel *ngIf="countdowns.length">
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<span translate>Countdowns</span>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-list>
|
||||
<mat-list-item
|
||||
*ngFor="let countdown of countdowns"
|
||||
class="larger-mat-list-item"
|
||||
[ngClass]="{ projected: isProjected(countdown) }"
|
||||
>
|
||||
<button type="button" mat-icon-button (click)="project(countdown)">
|
||||
<mat-icon>videocam</mat-icon>
|
||||
</button>
|
||||
{{ countdown.getTitle() | translate }}
|
||||
<os-countdown-controls
|
||||
class="dynamic-list-entry"
|
||||
[countdown]="countdown"
|
||||
[projector]="projector.projector"
|
||||
(editEvent)="openCountdownDialog($event)"
|
||||
></os-countdown-controls>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
<mat-action-row>
|
||||
<button type="button" mat-icon-button routerLink="/projectors/countdowns">
|
||||
<mat-icon>edit</mat-icon>
|
||||
<button type="button" mat-button (click)="openCountdownDialog()">
|
||||
<mat-icon>add</mat-icon>
|
||||
<span translate>Add countdown</span>
|
||||
</button>
|
||||
</mat-action-row>
|
||||
</mat-expansion-panel>
|
||||
|
@ -121,3 +121,15 @@
|
||||
.drop-list.cdk-drop-list-dragging .drop-list-entry:not(.cdk-drag-placeholder) {
|
||||
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.larger-mat-list-item {
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.larger-mat-list-item + .larger-mat-list-item {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.dynamic-list-entry {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { MatSnackBar, MatDialog } from '@angular/material';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -11,6 +11,11 @@ import {
|
||||
} from 'app/core/repositories/projector/projector-repository.service';
|
||||
import { ViewProjector } from '../../models/view-projector';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { Countdown } from 'app/shared/models/core/countdown';
|
||||
import { CountdownDialogComponent, CountdownData } from '../countdown-dialog/countdown-dialog.component';
|
||||
import { CurrentListOfSpeakersSlideService } from '../../services/current-list-of-of-speakers-slide.service';
|
||||
import { CurrentSpeakerChyronSlideService } from '../../services/current-speaker-chyron-slide.service';
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
import { ProjectorService } from 'app/core/core-services/projector.service';
|
||||
import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop';
|
||||
import { ProjectorElement } from 'app/shared/models/core/projector';
|
||||
@ -20,8 +25,6 @@ import { ProjectorMessageRepositoryService } from 'app/core/repositories/project
|
||||
import { ViewProjectorMessage } from 'app/site/projector/models/view-projector-message';
|
||||
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
||||
import { Projectable } from 'app/site/base/projectable';
|
||||
import { CurrentListOfSpeakersSlideService } from '../../services/current-list-of-of-speakers-slide.service';
|
||||
import { CurrentSpeakerChyronSlideService } from '../../services/current-speaker-chyron-slide.service';
|
||||
|
||||
/**
|
||||
* The projector detail view.
|
||||
@ -61,6 +64,7 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
||||
titleService: Title,
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
private dialog: MatDialog,
|
||||
private repo: ProjectorRepositoryService,
|
||||
private route: ActivatedRoute,
|
||||
private projectorService: ProjectorService,
|
||||
@ -68,7 +72,8 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
||||
private countdownRepo: CountdownRepositoryService,
|
||||
private messageRepo: ProjectorMessageRepositoryService,
|
||||
private currentListOfSpeakersSlideService: CurrentListOfSpeakersSlideService,
|
||||
private currentSpeakerChyronService: CurrentSpeakerChyronSlideService
|
||||
private currentSpeakerChyronService: CurrentSpeakerChyronSlideService,
|
||||
private durationService: DurationService
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
|
||||
@ -170,4 +175,63 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
||||
public toggleChyron(): void {
|
||||
this.currentSpeakerChyronService.toggleOn(this.projector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the countdown dialog
|
||||
*
|
||||
* @param viewCountdown optional existing countdown to edit
|
||||
*/
|
||||
public openCountdownDialog(viewCountdown?: ViewCountdown): void {
|
||||
let countdownData: CountdownData = {
|
||||
title: '',
|
||||
description: '',
|
||||
duration: '',
|
||||
count: this.countdowns.length
|
||||
};
|
||||
|
||||
if (viewCountdown) {
|
||||
countdownData = {
|
||||
title: viewCountdown.title,
|
||||
description: viewCountdown.description,
|
||||
duration: this.durationService.durationToString(viewCountdown.default_time, 'm')
|
||||
};
|
||||
}
|
||||
|
||||
const dialogRef = this.dialog.open(CountdownDialogComponent, {
|
||||
data: countdownData,
|
||||
maxHeight: '90vh',
|
||||
width: '400px',
|
||||
maxWidth: '90vw',
|
||||
disableClose: true
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.submitCountdown(result, viewCountdown);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to send a countdown
|
||||
*
|
||||
* @param data the countdown data to send
|
||||
* @param viewCountdown optional existing countdown to update
|
||||
*/
|
||||
public submitCountdown(data: CountdownData, viewCountdown?: ViewCountdown): void {
|
||||
const defaultTime = this.durationService.stringToDuration(data.duration, 'm');
|
||||
|
||||
const sendData = new Countdown({
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
default_time: defaultTime,
|
||||
countdown_time: viewCountdown && viewCountdown.running ? null : defaultTime
|
||||
});
|
||||
|
||||
if (viewCountdown) {
|
||||
this.countdownRepo.update(sendData, viewCountdown).then(() => {}, this.raiseError);
|
||||
} else {
|
||||
this.countdownRepo.create(sendData).then(() => {}, this.raiseError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,14 @@
|
||||
|
||||
<mat-card *ngIf="!projectorToCreate && projectors && projectors.length > 1">
|
||||
<span translate>
|
||||
Reference projector for current list of speakers:
|
||||
</span>
|
||||
Reference projector for current list of speakers: </span
|
||||
>
|
||||
<mat-form-field>
|
||||
<mat-select [disabled]="!!editId" [value]="projectors.length ? projectors[0].reference_projector_id : null" (selectionChange)="onSelectReferenceProjector($event)">
|
||||
<mat-select
|
||||
[disabled]="!!editId"
|
||||
[value]="projectors.length ? projectors[0].reference_projector_id : null"
|
||||
(selectionChange)="onSelectReferenceProjector($event)"
|
||||
>
|
||||
<mat-option *ngFor="let projector of projectors" [value]="projector.id">
|
||||
{{ projector.getTitle() | translate }}
|
||||
</mat-option>
|
||||
@ -33,7 +37,7 @@
|
||||
<form [formGroup]="createForm" (keydown)="keyDownFunction($event)">
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="name" matInput placeholder="{{'Name' | translate}}" required>
|
||||
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
||||
<mat-hint *ngIf="!createForm.controls.name.valid">
|
||||
<span translate>Required</span>
|
||||
</mat-hint>
|
||||
@ -58,16 +62,16 @@
|
||||
{{ projector.name | translate }}
|
||||
</ng-container>
|
||||
<ng-container class="meta-text-block-action-row" *ngIf="canManage">
|
||||
<button mat-icon-button *ngIf="editId !== projector.id" (click)=onEditButton(projector)>
|
||||
<button mat-icon-button *ngIf="editId !== projector.id" (click)="onEditButton(projector)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button *ngIf="editId === projector.id" (click)=onCancelButton(projector)>
|
||||
<button mat-icon-button *ngIf="editId === projector.id" (click)="onCancelButton(projector)">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button *ngIf="editId === projector.id" (click)=onSaveButton(projector)>
|
||||
<button mat-icon-button *ngIf="editId === projector.id" (click)="onSaveButton(projector)">
|
||||
<mat-icon>save</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" (click)=onDeleteButton(projector)>
|
||||
<button mat-icon-button color="warn" (click)="onDeleteButton(projector)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
@ -81,7 +85,7 @@
|
||||
<form [formGroup]="updateForm" (keydown)="keyDownFunction($event, projector)">
|
||||
<!-- Name field -->
|
||||
<mat-form-field>
|
||||
<input formControlName="name" matInput placeholder="{{'Name' | translate}}" required>
|
||||
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
||||
<mat-hint *ngIf="!updateForm.controls.name.valid">
|
||||
<span translate>Required</span>
|
||||
</mat-hint>
|
||||
@ -94,7 +98,13 @@
|
||||
{{ ratio }}
|
||||
</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<mat-slider [thumbLabel]="true" formControlName="width" min="800" max="3840" step="10"></mat-slider>
|
||||
<mat-slider
|
||||
[thumbLabel]="true"
|
||||
formControlName="width"
|
||||
min="800"
|
||||
max="3840"
|
||||
step="10"
|
||||
></mat-slider>
|
||||
{{ updateForm.value.width }}
|
||||
|
||||
<!-- colors -->
|
||||
@ -160,8 +170,4 @@
|
||||
<mat-icon>note</mat-icon>
|
||||
<span translate>Projector messages</span>
|
||||
</button>
|
||||
<button type="button" mat-menu-item routerLink="/projectors/countdowns">
|
||||
<mat-icon>alarm</mat-icon>
|
||||
<span translate>Countdowns</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
||||
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
||||
import { CountdownListComponent } from './components/countdown-list/countdown-list.component';
|
||||
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
||||
|
||||
const routes: Routes = [
|
||||
@ -15,10 +14,6 @@ const routes: Routes = [
|
||||
path: 'detail/:id',
|
||||
component: ProjectorDetailComponent
|
||||
},
|
||||
{
|
||||
path: 'countdowns',
|
||||
component: CountdownListComponent
|
||||
},
|
||||
{
|
||||
path: 'messages',
|
||||
component: ProjectorMessageListComponent
|
||||
|
@ -5,18 +5,19 @@ import { ProjectorRoutingModule } from './projector-routing.module';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
||||
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
||||
import { CountdownListComponent } from './components/countdown-list/countdown-list.component';
|
||||
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
||||
import { CountdownControlsComponent } from './components/countdown-controls/countdown-controls.component';
|
||||
import { CountdownDialogComponent } from './components/countdown-dialog/countdown-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, ProjectorRoutingModule, SharedModule],
|
||||
declarations: [
|
||||
ProjectorListComponent,
|
||||
ProjectorDetailComponent,
|
||||
CountdownListComponent,
|
||||
ProjectorMessageListComponent,
|
||||
CountdownControlsComponent
|
||||
]
|
||||
CountdownControlsComponent,
|
||||
CountdownDialogComponent
|
||||
],
|
||||
entryComponents: [CountdownDialogComponent]
|
||||
})
|
||||
export class ProjectorModule {}
|
||||
|
@ -11,7 +11,8 @@
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
|
||||
h1, h3.accent {
|
||||
h1,
|
||||
h3.accent {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
@ -19,7 +20,8 @@
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
.accent, .accent-text {
|
||||
.accent,
|
||||
.accent-text {
|
||||
color: mat-color($accent);
|
||||
}
|
||||
|
||||
@ -27,10 +29,6 @@
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
.projected .mat-icon {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
//custom table header for search button, filtering and more. Used in ListViews
|
||||
.custom-table-header {
|
||||
background: mat-color($background, background);
|
||||
@ -42,7 +40,9 @@
|
||||
font-size: 75%;
|
||||
display: block;
|
||||
}
|
||||
.error, .warn {
|
||||
|
||||
.error,
|
||||
.warn {
|
||||
color: mat-color($warn);
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,4 @@ $openslides-primary: mat-palette($openslides-green);
|
||||
$openslides-accent: mat-palette($mat-amber);
|
||||
$openslides-warn: mat-palette($mat-red);
|
||||
|
||||
$openslides-green-theme: mat-light-theme(
|
||||
$openslides-primary,
|
||||
$openslides-accent,
|
||||
$openslides-warn
|
||||
)
|
||||
$openslides-green-theme: mat-light-theme($openslides-primary, $openslides-accent, $openslides-warn);
|
||||
|
@ -651,7 +651,6 @@ button.mat-menu-item.selected {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
|
||||
.queue {
|
||||
.mat-expansion-panel-body {
|
||||
padding: 0 !important;
|
||||
|
Loading…
Reference in New Issue
Block a user