diff --git a/projects/ars/src/lib/ars.module.ts b/projects/ars/src/lib/ars.module.ts index d74036d7dda3a80be31e3b3075deffc395ec8e4a..03efa211e66ea89466d009bff2aa9662e512889a 100644 --- a/projects/ars/src/lib/ars.module.ts +++ b/projects/ars/src/lib/ars.module.ts @@ -17,6 +17,20 @@ import { ButtonBaseDirective } from './components/content/menu/ButtonBase.direct import { MaterialBtnComponent } from './components/style/menu/material-btn/material-btn.component'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; +import { ArsComposeHostDirective } from './compose/ars-compose-host.directive'; +import { MatButtonComponent } from './compose/elements/mat-button/mat-button.component'; +import { MatToggleComponent } from './compose/elements/mat-toggle/mat-toggle.component'; +import { MatChipListComponent } from './compose/elements/mat-chip-list/mat-chip-list.component'; +import { MatDatePickerComponent } from './compose/elements/mat-date-picker/mat-date-picker.component'; +import { MatMenuItemComponent } from './compose/elements/mat-menu-item/mat-menu-item.component'; +import { CommonModule } from '@angular/common'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatInputModule } from '@angular/material/input'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; @NgModule({ declarations: [ @@ -35,13 +49,27 @@ import { MatIconModule } from '@angular/material/icon'; MenuWrapperDirective, ButtonWrapperDirective, ButtonBaseDirective, - MaterialBtnComponent + MaterialBtnComponent, + ArsComposeHostDirective, + MatButtonComponent, + MatToggleComponent, + MatChipListComponent, + MatDatePickerComponent, + MatMenuItemComponent ], - imports: [ + imports:[ MatIconModule, - MatButtonModule + MatButtonModule, + CommonModule, + MatChipsModule, + MatFormFieldModule, + MatDatepickerModule, + TranslateModule, + MatMenuModule, + MatInputModule, + MatSlideToggleModule ], - exports: [ + exports:[ ArsComponent, FullScreenOverlayComponent, WrapperDirective, @@ -56,7 +84,8 @@ import { MatIconModule } from '@angular/material/icon'; MenuWrapperDirective, ButtonWrapperDirective, ButtonBaseDirective, - MaterialBtnComponent + MaterialBtnComponent, + ArsComposeHostDirective ] }) export class ArsModule { } diff --git a/projects/ars/src/lib/compose/ars-compose-host.directive.spec.ts b/projects/ars/src/lib/compose/ars-compose-host.directive.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d2669b0ff691386c0e53c1f7ad7b264de63ea4d3 --- /dev/null +++ b/projects/ars/src/lib/compose/ars-compose-host.directive.spec.ts @@ -0,0 +1,8 @@ +// import { ArsComposeHostDirective } from './ars-compose-host.directive'; +// +// describe('ArsComposeHostDirective', () => { +// it('should create an instance', () => { +// const directive = new ArsComposeHostDirective(); +// expect(directive).toBeTruthy(); +// }); +// }); diff --git a/projects/ars/src/lib/compose/ars-compose-host.directive.ts b/projects/ars/src/lib/compose/ars-compose-host.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..02b6f22b11941ebae1f4140eb6af2b716233a850 --- /dev/null +++ b/projects/ars/src/lib/compose/ars-compose-host.directive.ts @@ -0,0 +1,20 @@ +import { AfterViewInit, Directive, EventEmitter, Input, Output, ViewContainerRef } from '@angular/core'; + +@Directive({ + selector:'[arsComposeHost]' +}) +export class ArsComposeHostDirective implements AfterViewInit{ + + @Input('ident') ident: any; + @Output() onAfterViewInit: EventEmitter<void> = new EventEmitter<void>(); + + constructor( + public viewContainerRef: ViewContainerRef + ){ + } + + ngAfterViewInit(){ + this.onAfterViewInit.emit(); + } + +} diff --git a/projects/ars/src/lib/compose/elements/mat-button/ars-mat-button-config.ts b/projects/ars/src/lib/compose/elements/mat-button/ars-mat-button-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a91fb075af37dfc9235be35371685079e760f71 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-button/ars-mat-button-config.ts @@ -0,0 +1,14 @@ +import { TranslateService } from '@ngx-translate/core'; +import { InjectionToken } from '@angular/core'; + + +export interface ArsMatButtonConfig{ + translate: TranslateService; + title: string; + icon?: string; + isSVGIcon?: boolean; + callback?: (e?: MouseEvent) => void; + condition?: () => boolean; +} + +export const ARS_MAT_BUTTON_CONFIG = new InjectionToken('ARS_MAT_TOGGLE_DATA'); diff --git a/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.html b/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2ccce9d3db5821c9aa352bbb7da94f14daf0c056 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.html @@ -0,0 +1,10 @@ +<button mat-flat-button class="btn" (click)="data.callback($event)"> + <ng-container *ngIf="data.icon"> + <mat-icon *ngIf="!data.isSVGIcon" style="padding-right:8px;"> + {{data.icon}} + </mat-icon> + <mat-icon *ngIf="data.isSVGIcon" [svgIcon]="data.icon" style="padding-right:8px;"> + </mat-icon> + </ng-container> + <span>{{data.title}}</span> +</button> diff --git a/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.scss b/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..3b872aa451d0e3f47cdeda3f24c081d0fb3ffe9e --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.scss @@ -0,0 +1,6 @@ +:host{ + .btn{ + padding:8px; + box-sizing:border-box; + } +} diff --git a/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.spec.ts b/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..27f717e88b744236b0d7efd61ede551b21f8ab19 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.spec.ts @@ -0,0 +1,25 @@ +// import { ComponentFixture, TestBed } from '@angular/core/testing'; +// +// import { MatButtonComponent } from './mat-button.component'; +// +// describe('MatButtonComponent', () => { +// let component: MatButtonComponent; +// let fixture: ComponentFixture<MatButtonComponent>; +// +// beforeEach(async () => { +// await TestBed.configureTestingModule({ +// declarations: [ MatButtonComponent ] +// }) +// .compileComponents(); +// }); +// +// beforeEach(() => { +// fixture = TestBed.createComponent(MatButtonComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); +// +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.ts b/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..a069df0bd0f6da1310db711035e5b5762b440ef1 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-button/mat-button.component.ts @@ -0,0 +1,19 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { ARS_MAT_BUTTON_CONFIG, ArsMatButtonConfig } from './ars-mat-button-config'; + +@Component({ + selector:'ars-mat-button', + templateUrl:'./mat-button.component.html', + styleUrls:['./mat-button.component.scss'] +}) +export class MatButtonComponent implements OnInit{ + + constructor( + @Inject(ARS_MAT_BUTTON_CONFIG) public data: ArsMatButtonConfig + ){ + } + + ngOnInit(): void{ + } + +} diff --git a/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list-config.ts b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..90b110763a8434d1e4c039f32f745ca01a5d2117 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list-config.ts @@ -0,0 +1,26 @@ +import { InjectionToken } from '@angular/core'; +import { ArsObserver } from '../../../models/util/ars-observer'; + +export interface ArsMatChipConfig{ + title: string; + icon?: string; + isSVGIcon?: boolean; + color?: string; + onSelect?: (e: ArsMatChipConfig) => void; + condition?: () => boolean; +} + +export interface ArsMatChipListConfig{ + list: ArsObserver<ArsMatChipConfig[]>; + def?: string[]; + onSelect?: (e: ArsObserver<ArsMatChipConfig[]>) => void; + type?: ArsMatChipListType; +} + +export enum ArsMatChipListType{ + SELECT, + SELECTION, + TOGGLE +} + +export const ARS_MAT_CHIP_LIST_CONFIG = new InjectionToken('ARS_MAT_CHIP_LIST_CONFIG'); diff --git a/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.html b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2fd287b66a977a91ab2d4938c4b60ec80d6a85bb --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.html @@ -0,0 +1,11 @@ +<ars-row class="chip-list-wrp"> + <mat-chip-list class="chip-list"> + <ng-container *ngFor="let chip of data.list.get()"> + <mat-chip *ngIf="!chip.condition||chip.condition()" (click)="select(chip)"> + <mat-icon *ngIf="chip.icon&&!chip.isSVGIcon" style="padding-right:4px;">{{chip.icon}}</mat-icon> + <mat-icon *ngIf="chip.icon&&chip.isSVGIcon" style="padding-right:4px;" [svgIcon]="chip.icon"></mat-icon> + <span>{{chip.title}}</span> + </mat-chip> + </ng-container> + </mat-chip-list> +</ars-row> diff --git a/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.scss b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..a74681aa745d7b55f1c87666c1880b88924566f5 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.scss @@ -0,0 +1,24 @@ +:host { + .chip-list-wrp{ + padding:8px; + box-sizing:border-box; + } + .chip-list { + @for $i from 1 through 20 { + :nth-child(#{$i}n) { + animation-delay: #{$i * 0.1 + 0.5}s; + } + } + mat-chip{ + animation:fade-bot 1s ease-in-out both; + } + } + @keyframes fade-bot { + 0% { + opacity:0; + } + 100% { + opacity:1; + } + } +} diff --git a/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.spec.ts b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..850dd85671e158598b90dccb069d2e60cdb6f8c5 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.spec.ts @@ -0,0 +1,25 @@ +// import { ComponentFixture, TestBed } from '@angular/core/testing'; +// +// import { MatChipListComponent } from './mat-chip-list.component'; +// +// describe('MatChipListComponent', () => { +// let component: MatChipListComponent; +// let fixture: ComponentFixture<MatChipListComponent>; +// +// beforeEach(async () => { +// await TestBed.configureTestingModule({ +// declarations: [ MatChipListComponent ] +// }) +// .compileComponents(); +// }); +// +// beforeEach(() => { +// fixture = TestBed.createComponent(MatChipListComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); +// +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.ts b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a938fee5dd753ac46126068a85145ab51b581f7 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-chip-list/mat-chip-list.component.ts @@ -0,0 +1,30 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { ARS_MAT_CHIP_LIST_CONFIG, ArsMatChipConfig, ArsMatChipListConfig } from './mat-chip-list-config'; +import { ArsAnchor } from '../../../models/util/ars-observer'; + +@Component({ + selector:'app-mat-chip-list', + templateUrl:'./mat-chip-list.component.html', + styleUrls:['./mat-chip-list.component.scss'] +}) +export class MatChipListComponent implements OnInit{ + + elementAnchor: ArsAnchor<ArsMatChipConfig[]>; + + constructor( + @Inject(ARS_MAT_CHIP_LIST_CONFIG) public data: ArsMatChipListConfig + ){ + } + + ngOnInit(): void{ + this.elementAnchor = this.data.list.createAnchor(); + } + + select(chip: ArsMatChipConfig){ + chip.onSelect(chip); + if (this.data.onSelect){ + this.data.onSelect(this.data.list); + } + } + +} diff --git a/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker-config.ts b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..bed10b6544a2634ed1a219f3e80d18b49e79508a --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker-config.ts @@ -0,0 +1,16 @@ +import { InjectionToken } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { MatFormFieldAppearance } from '@angular/material/form-field'; +import { ArsObserver } from '../../../models/util/ars-observer'; + + +export interface ArsMatDatePickerConfig{ + translate: TranslateService, + appearance?: MatFormFieldAppearance, + title: string, + callback?: (e: Date) => void, + change: ArsObserver<Date> +} + + +export const ARS_MAT_DATE_PICKER = new InjectionToken('ARS_MAT_DATE_PICKER'); diff --git a/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.html b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2557715f69d53cd941724ff1a9a88ab1003c1e62 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.html @@ -0,0 +1,6 @@ +<mat-form-field [appearance]="data.appearance?data.appearance:'fill'" class="ars-mat-date-picker"> + <mat-label>{{data.title | translate}}</mat-label> + <input matInput [matDatepicker]="picker" (dateChange)="dateChange($event)"> + <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle> + <mat-datepicker #picker></mat-datepicker> +</mat-form-field> diff --git a/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.scss b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..9d340d5c5e8c526c9e2d5aba3b7238f640a64bfa --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.scss @@ -0,0 +1,16 @@ +:host{ + .ars-mat-date-picker{ + animation:ars-mat-date-picker-animation 1s ease-in-out both; + box-sizing:border-box; + padding:0px 16px; + width:100%; + } + @keyframes ars-mat-date-picker-animation { + 0% { + opacity:0; + } + 100% { + opacity:1; + } + } +} diff --git a/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.spec.ts b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..174cecd85e5a96f6cfca197008f4ebc22b5b8d4b --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.spec.ts @@ -0,0 +1,25 @@ +// import { ComponentFixture, TestBed } from '@angular/core/testing'; +// +// import { MatDatePickerComponent } from './mat-date-picker.component'; +// +// describe('MatDatePickerComponent', () => { +// let component: MatDatePickerComponent; +// let fixture: ComponentFixture<MatDatePickerComponent>; +// +// beforeEach(async () => { +// await TestBed.configureTestingModule({ +// declarations: [ MatDatePickerComponent ] +// }) +// .compileComponents(); +// }); +// +// beforeEach(() => { +// fixture = TestBed.createComponent(MatDatePickerComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); +// +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.ts b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..7397742071eab598fde616102b77c8df3598cd12 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-date-picker/mat-date-picker.component.ts @@ -0,0 +1,24 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { ARS_MAT_DATE_PICKER, ArsMatDatePickerConfig } from './mat-date-picker-config'; +import { MatDatepickerInputEvent } from '@angular/material/datepicker'; + +@Component({ + selector:'ars-mat-date-picker', + templateUrl:'./mat-date-picker.component.html', + styleUrls:['./mat-date-picker.component.scss'] +}) +export class MatDatePickerComponent implements OnInit{ + + constructor( + @Inject(ARS_MAT_DATE_PICKER) public data: ArsMatDatePickerConfig + ){ + } + + ngOnInit(): void{ + } + + dateChange(e: MatDatepickerInputEvent<any>){ + console.log(e); + } + +} diff --git a/projects/ars/src/lib/compose/elements/mat-menu-item/ars-mat-menu-item-config.ts b/projects/ars/src/lib/compose/elements/mat-menu-item/ars-mat-menu-item-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..f82aa367bdb68dcec03e88f48c0f9c5e83077a0f --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-menu-item/ars-mat-menu-item-config.ts @@ -0,0 +1,16 @@ +import { TranslateService } from '@ngx-translate/core'; +import { InjectionToken } from '@angular/core'; + +export interface ArsMatMenuItemConfig{ + translate: TranslateService; + icon: string; + isSVGIcon?: boolean; + text: string; + color?: string; + iconColor?: string; + callback?: (e?: MouseEvent) => void; + condition?: () => boolean; + routerLink?: string; +} + +export const ARS_MAT_MENU_ITEM_DATA = new InjectionToken('ARS_MAT_MENU_ITEM_DATA'); diff --git a/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.html b/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.html new file mode 100644 index 0000000000000000000000000000000000000000..113f3aea073e09f6f7a97050d3acba8a11074eb8 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.html @@ -0,0 +1,15 @@ +<button mat-menu-item + tabindex="0" + (click)="action($event)" + *ngIf="data.condition()"> + <ng-container *ngIf="data.isSVGIcon"> + <mat-icon svgIcon="{{data.icon}}"> + </mat-icon> + </ng-container> + <ng-container *ngIf="!data.isSVGIcon"> + <mat-icon [ngStyle]="{'color':data.iconColor}"> + {{data.icon}} + </mat-icon> + </ng-container> + <span>{{data.text | translate}}</span> +</button> diff --git a/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.scss b/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.spec.ts b/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6377f63f53a0dd378e0f9d74669b11a2a661eac --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.spec.ts @@ -0,0 +1,25 @@ +// import { ComponentFixture, TestBed } from '@angular/core/testing'; +// +// import { MatMenuItemComponent } from './mat-menu-item.component'; +// +// describe('MatMenuItemComponent', () => { +// let component: MatMenuItemComponent; +// let fixture: ComponentFixture<MatMenuItemComponent>; +// +// beforeEach(async () => { +// await TestBed.configureTestingModule({ +// declarations: [ MatMenuItemComponent ] +// }) +// .compileComponents(); +// }); +// +// beforeEach(() => { +// fixture = TestBed.createComponent(MatMenuItemComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); +// +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.ts b/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..18c79ea9486bb5301e2a5a1a8042172745442ac2 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-menu-item/mat-menu-item.component.ts @@ -0,0 +1,34 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { ARS_MAT_MENU_ITEM_DATA, ArsMatMenuItemConfig } from './ars-mat-menu-item-config'; +import { TranslateService } from '@ngx-translate/core'; +import { Router } from '@angular/router'; + +@Component({ + selector:'ars-mat-menu-item', + templateUrl:'./mat-menu-item.component.html', + styleUrls:['./mat-menu-item.component.scss'] +}) +export class MatMenuItemComponent implements OnInit{ + + public translate: TranslateService; + + constructor( + public router: Router, + @Inject(ARS_MAT_MENU_ITEM_DATA) public data: ArsMatMenuItemConfig + ){ + this.translate = data.translate; + } + + ngOnInit(): void{ + } + + public action(e: MouseEvent){ + if (this.data.callback){ + this.data.callback(e); + } + if (this.data.routerLink){ + this.router.navigateByUrl(this.data.routerLink); + } + } + +} diff --git a/projects/ars/src/lib/compose/elements/mat-toggle/ars-mat-toggle-config.ts b/projects/ars/src/lib/compose/elements/mat-toggle/ars-mat-toggle-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..a37af0a6a7e2146c3407c8b7441badfd727d129e --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-toggle/ars-mat-toggle-config.ts @@ -0,0 +1,18 @@ +import { TranslateService } from '@ngx-translate/core'; +import { InjectionToken } from '@angular/core'; +import { ArsObserver } from '../../../models/util/ars-observer'; + +export interface ArsMatToggleConfig{ + translate: TranslateService; + textActivated: string; + textDeactivated: string; + colorActivated?: string; + colorDeactivated?: string; + callback?: (e?: MouseEvent) => void; + condition?: () => boolean; + checked: ArsObserver<boolean>; + checkAsToggle?: boolean; + height?: number; +} + +export const ARS_MAT_TOGGLE_CONFIG = new InjectionToken('ARS_MAT_TOGGLE_DATA'); diff --git a/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.html b/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.html new file mode 100644 index 0000000000000000000000000000000000000000..3b81e5496bea4db3dddd60f43559d68e1bd9c063 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.html @@ -0,0 +1,11 @@ +<button mat-menu-item + tabindex="0" + (click)="action($event)" + *ngIf="data.condition()"> + <mat-slide-toggle + (click)="action($event)" + style="margin-left:-5px;margin-right:9px;" [checked]="state.value"> + </mat-slide-toggle> + <span *ngIf="state.value">{{data.textActivated | translate}}</span> + <span *ngIf="!state.value">{{data.textDeactivated | translate}}</span> +</button> diff --git a/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.scss b/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.spec.ts b/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ff730fa2458ed0143aab11165bbeb01f70ec9510 --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.spec.ts @@ -0,0 +1,25 @@ +// import { ComponentFixture, TestBed } from '@angular/core/testing'; +// +// import { MatToggleComponent } from './mat-toggle.component'; +// +// describe('MatToggleComponent', () => { +// let component: MatToggleComponent; +// let fixture: ComponentFixture<MatToggleComponent>; +// +// beforeEach(async () => { +// await TestBed.configureTestingModule({ +// declarations: [ MatToggleComponent ] +// }) +// .compileComponents(); +// }); +// +// beforeEach(() => { +// fixture = TestBed.createComponent(MatToggleComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); +// +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.ts b/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..419d22255c81531d2cc509f075ca79a9919123ce --- /dev/null +++ b/projects/ars/src/lib/compose/elements/mat-toggle/mat-toggle.component.ts @@ -0,0 +1,35 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { ARS_MAT_TOGGLE_CONFIG, ArsMatToggleConfig } from './ars-mat-toggle-config'; +import { ArsAnchor } from '../../../models/util/ars-observer'; + +@Component({ + selector:'ars-mat-toggle', + templateUrl:'./mat-toggle.component.html', + styleUrls:['./mat-toggle.component.scss'] +}) +export class MatToggleComponent implements OnInit{ + + public translate: TranslateService; + public state: ArsAnchor<boolean>; + + constructor( + @Inject(ARS_MAT_TOGGLE_CONFIG) public data: ArsMatToggleConfig + ){ + this.translate = data.translate; + this.state = data.checked.createAnchor(); + } + + ngOnInit(){ + } + + public action(e: MouseEvent){ + if (this.data.checkAsToggle){ + this.data.checked.set(!this.data.checked.get()); + } + if (this.data.callback){ + this.data.callback(e); + } + } + +} diff --git a/projects/ars/src/lib/models/util/ars-observer.ts b/projects/ars/src/lib/models/util/ars-observer.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8935e76eff8ebd1b150cebf966044a7c1422f95 --- /dev/null +++ b/projects/ars/src/lib/models/util/ars-observer.ts @@ -0,0 +1,88 @@ +/** + * ArsAnchor + * When created with ArsObserver.createAnchor the value of this + * object (value:E) will change when the current value of the + * observer, which created this object, changes. + * + * Use: + * It is preferred to use variables instead of functions for + * 'attributes with changing values'. + * + * E.g.: @angular/common structural directive *ngIf="" + * + * *ngIf="getState()" + * the function getState() will be applied + * for every detected change, even if the value returned by the + * function is the same. Depending on the implementation of that + * function, it might decrease performance. + * + * *ngIf="state" + * does not apply a function + * + */ +export class ArsAnchor<E> { + public value:E; + constructor(private rel:()=>void){} + public release():void{ + this.rel(); + } +} + +/** + * ArsObserver + * State-Change-Listener + */ +export class ArsObserver<E> { + + public static build<E>(e:(a:ArsObserver<E>)=>void):ArsObserver<E>{ + const obs=new ArsObserver<E>(); + e(obs); + return obs; + } + + private previous:E; + private current:E; + private listener:((e:ArsObserver<E>)=>void)[]=[]; + constructor(e?:E){ + if(e)this.current=e; + } + + public set(e:E){ + this.previous=this.current; + this.current=e; + this.listener.forEach(listener=>listener(this)); + } + + public get():E{ + return this.current; + } + + public getPrevious():E{ + return this.previous; + } + + public empty():boolean{ + return this.current == null; + } + + public onChange(consumer:(e:ArsObserver<E>)=>void,run?:boolean):()=>void { + this.listener.push(consumer); + if(run)consumer(this); + return ()=>this.listener + =this.listener.splice(this.listener.indexOf(consumer)); + } + + // this works for some reason + public createAnchor():ArsAnchor<E>{ + const anchor:ArsAnchor<E>=new ArsAnchor<E>( + this.onChange(e=>anchor.value=e.get())); + return anchor; + } + + public map<A>(consumer:(left:ArsObserver<E>,right?:ArsObserver<A>)=>A,run?:boolean):ArsObserver<A>{ + const obs=new ArsObserver<A>(); + this.onChange(e=>obs.set(consumer(e,obs)),run); + return obs; + } + +} diff --git a/projects/ars/src/lib/services/ars-compose.service.spec.ts b/projects/ars/src/lib/services/ars-compose.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d1d1107135cdc675d8a6dd256050c55f90e0df40 --- /dev/null +++ b/projects/ars/src/lib/services/ars-compose.service.spec.ts @@ -0,0 +1,16 @@ +// import { TestBed } from '@angular/core/testing'; +// +// import { ArsComposeService } from './ars-compose.service'; +// +// describe('ArsComposeService', () => { +// let service: ArsComposeService; +// +// beforeEach(() => { +// TestBed.configureTestingModule({}); +// service = TestBed.inject(ArsComposeService); +// }); +// +// it('should be created', () => { +// expect(service).toBeTruthy(); +// }); +// }); diff --git a/projects/ars/src/lib/services/ars-compose.service.ts b/projects/ars/src/lib/services/ars-compose.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..36ad1aba090d7bd5a2180c3087183736ca2afffd --- /dev/null +++ b/projects/ars/src/lib/services/ars-compose.service.ts @@ -0,0 +1,113 @@ +import { + ComponentFactory, + ComponentFactoryResolver, + ComponentRef, + Injectable, + InjectionToken, + Injector, + Type, + ViewContainerRef +} from '@angular/core'; +import { PortalInjector } from '@angular/cdk/portal'; +import { ArsComposeHostDirective } from '../compose/ars-compose-host.directive'; +import { ARS_MAT_MENU_ITEM_DATA, ArsMatMenuItemConfig } from '../compose/elements/mat-menu-item/ars-mat-menu-item-config'; +import { ARS_MAT_CHIP_LIST_CONFIG, ArsMatChipListConfig } from '../compose/elements/mat-chip-list/mat-chip-list-config'; +import { ARS_MAT_DATE_PICKER, ArsMatDatePickerConfig } from '../compose/elements/mat-date-picker/mat-date-picker-config'; +import { ARS_MAT_TOGGLE_CONFIG, ArsMatToggleConfig } from '../compose/elements/mat-toggle/ars-mat-toggle-config'; +import { ARS_MAT_BUTTON_CONFIG, ArsMatButtonConfig } from '../compose/elements/mat-button/ars-mat-button-config'; +import { MatButtonComponent } from '../compose/elements/mat-button/mat-button.component'; +import { MatChipListComponent } from '../compose/elements/mat-chip-list/mat-chip-list.component'; +import { MatDatePickerComponent } from '../compose/elements/mat-date-picker/mat-date-picker.component'; +import { MatToggleComponent } from '../compose/elements/mat-toggle/mat-toggle.component'; +import { MatMenuItemComponent } from '../compose/elements/mat-menu-item/mat-menu-item.component'; +import { ArsAnchor, ArsObserver } from '../models/util/ars-observer'; + +export interface ArsComposeBuilder{ + menuItem(i: ArsMatMenuItemConfig): void; + + chipList(i: ArsMatChipListConfig): void; + + datePicker(i: ArsMatDatePickerConfig): void; + + toggle(i: ArsMatToggleConfig): void; + + button(i: ArsMatButtonConfig): void; + + altToggle(onFalse: ArsMatMenuItemConfig, onTrue: ArsMatMenuItemConfig, obs: ArsObserver<boolean>, + condition: () => boolean): ArsObserver<boolean>; +} + +@Injectable({ + providedIn:'root' +}) +export class ArsComposeService{ + + constructor( + private cfr: ComponentFactoryResolver, + private injector: Injector + ){ + } + + builder(host: ArsComposeHostDirective, e: (e: ArsComposeBuilder) => void): ComponentRef<any>[]{ + let list: ComponentRef<any>[] = []; + const _this = this; + e({ + button(i: ArsMatButtonConfig){ + const ref = _this.build(host, MatButtonComponent, ARS_MAT_BUTTON_CONFIG, i); + list.push(ref); + }, + chipList(i: ArsMatChipListConfig){ + const ref = _this.build(host, MatChipListComponent, ARS_MAT_CHIP_LIST_CONFIG, i); + list.push(ref); + }, + datePicker(i: ArsMatDatePickerConfig){ + const ref = _this.build(host, MatDatePickerComponent, ARS_MAT_DATE_PICKER, i); + list.push(ref); + }, + menuItem(i: ArsMatMenuItemConfig){ + const ref = _this.build(host, MatMenuItemComponent, ARS_MAT_MENU_ITEM_DATA, i); + list.push(ref); + }, + toggle(i: ArsMatToggleConfig){ + const ref = _this.build(host, MatToggleComponent, ARS_MAT_TOGGLE_CONFIG, i); + list.push(ref); + }, + altToggle(onFalse: ArsMatMenuItemConfig, onTrue: ArsMatMenuItemConfig, + obs: ArsObserver<boolean>, condition: () => boolean): ArsObserver<boolean>{ + onFalse.condition = () => !obs.get() && condition(); + onTrue.condition = () => obs.get() && condition(); + this.menuItem(onFalse); + this.menuItem(onTrue); + return obs; + } + }); + return list; + } + + build<E>(host: ArsComposeHostDirective, component: Type<E>, token: InjectionToken<any>, data: any): ComponentRef<E>{ + return this.create( + host.viewContainerRef, + component, + this.createMap(token, data) + ); + } + + create<E>(vcr: ViewContainerRef, component: Type<E>, map: WeakMap<any, any>): ComponentRef<E>{ + return vcr.createComponent(this.resolve(component), null, this.createInjector(map)); + } + + private resolve<E>(component: Type<E>): ComponentFactory<E>{ + return this.cfr.resolveComponentFactory(component); + } + + private createInjector(map: WeakMap<any, any>): PortalInjector{ + return new PortalInjector(this.injector, map); + } + + public createMap(key: any, value: any){ + const map = new WeakMap<any, any>(); + map.set(key, value); + return map; + } + +} diff --git a/src/app/components/creator/_dialogs/room-name-settings/room-name-settings.component.html b/src/app/components/creator/_dialogs/room-name-settings/room-name-settings.component.html index 25a131cd29f8105283631f510026695efe313aa3..69cc542282ba7b4167869f1eed357595c014bdfb 100644 --- a/src/app/components/creator/_dialogs/room-name-settings/room-name-settings.component.html +++ b/src/app/components/creator/_dialogs/room-name-settings/room-name-settings.component.html @@ -10,7 +10,7 @@ maxlength="20" aria-labelledby="room-name"/> <mat-placeholder class="placeholder">{{ 'session.session-name' | translate }}</mat-placeholder> - <mat-hint align="end"><span aria-hidden="true">{{ editRoom.name.length }} / 20</span></mat-hint> + <mat-hint align="end"><span aria-hidden="true">{{ roomNameFormControl.value.length }} / 20</span></mat-hint> <mat-error *ngIf="roomNameFormControl.hasError('required') || roomNameFormControl.hasError('minlength') || roomNameFormControl.hasError('maxlength')"> diff --git a/src/app/components/creator/room-creator-page/room-creator-page.component.ts b/src/app/components/creator/room-creator-page/room-creator-page.component.ts index aeff1ca57338ae1a0fb83d667806a9416c57414d..a4d1c17977747bdf2abb46c22d1d766d371fc339 100644 --- a/src/app/components/creator/room-creator-page/room-creator-page.component.ts +++ b/src/app/components/creator/room-creator-page/room-creator-page.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Renderer2, OnDestroy, AfterContentInit } from '@angular/core'; +import { AfterContentInit, AfterViewInit, Component, ComponentRef, EventEmitter, OnDestroy, OnInit, Renderer2 } from '@angular/core'; import { RoomService } from '../../../services/http/room.service'; import { ActivatedRoute, Router } from '@angular/router'; import { RoomPageComponent } from '../../shared/room-page/room-page.component'; @@ -29,15 +29,23 @@ import { RoomDeleteComponent } from '../_dialogs/room-delete/room-delete.compone import { RoomDeleted } from '../../../models/events/room-deleted'; import { ProfanitySettingsComponent } from '../_dialogs/profanity-settings/profanity-settings.component'; import { RoomDescriptionSettingsComponent } from '../_dialogs/room-description-settings/room-description-settings.component'; +import { AuthenticationService } from '../../../services/http/authentication.service'; +import { User } from '../../../models/user'; +import { HeaderService } from '../../../services/util/header.service'; +import { ArsComposeService } from '../../../../../projects/ars/src/lib/services/ars-compose.service'; +import { UserRole } from '../../../models/user-roles.enum'; +import { Palette } from '../../../../theme/Theme'; +import { ArsObserver } from '../../../../../projects/ars/src/lib/models/util/ars-observer'; import { RoomNameSettingsComponent } from '../_dialogs/room-name-settings/room-name-settings.component'; @Component({ - selector: 'app-room-creator-page', - templateUrl: './room-creator-page.component.html', - styleUrls: ['./room-creator-page.component.scss'] + selector:'app-room-creator-page', + templateUrl:'./room-creator-page.component.html', + styleUrls:['./room-creator-page.component.scss'] }) -export class RoomCreatorPageComponent extends RoomPageComponent implements OnInit, OnDestroy, AfterContentInit { +export class RoomCreatorPageComponent extends RoomPageComponent implements OnInit, OnDestroy, AfterContentInit, AfterViewInit{ room: Room; + user: User; encodedShortId: string; updRoom: Room; commentThreshold: number; @@ -48,6 +56,7 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni commentCounterEmitSubscription: any; urlToCopy = `${window.location.protocol}//${window.location.hostname}/participant/room/`; headerInterface = null; + onDestroyListener: EventEmitter<void> = new EventEmitter<void>(); constructor(protected roomService: RoomService, protected notification: NotificationService, @@ -65,7 +74,10 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni private notificationService: NotificationService, private bonusTokenService: BonusTokenService, public router: Router, - public translationService: TranslateService) { + public translationService: TranslateService, + public authenticationService: AuthenticationService, + public headerService: HeaderService, + public composeService: ArsComposeService){ super(roomService, route, location, wsCommentService, commentService, eventService); this.commentCounterEmitSubscription = this.commentCounterEmit.subscribe(e => { this.titleService.attachTitle('(' + e + ')'); @@ -73,23 +85,124 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni langService.langEmitter.subscribe(lang => translateService.use(lang)); } - initNavigation() { - const navigation = {}; - const nav = (b, c) => navigation[b] = c; - nav('deleteRoom',()=>this.openDeleteRoomDialog()); - nav('profanityFilter',()=>this.toggleProfanityFilter()); - nav('editSessionDescription',()=>this.editSessionDescription()); - nav('roomBonusToken', () => this.showBonusTokenDialog()); - nav('moderator', () => this.showModeratorsDialog()); - nav('tags', () => this.showTagsDialog()); - nav('topicCloud', () => this.showTagCloud()); - nav('exportQuestions', () => this.exportQuestions()); - nav('deleteQuestions', () => this.deleteQuestions()); - nav('moderatorSettings', () => this.navigateModeratorSettings()); - this.headerInterface = this.eventService.on<string>('navigate').subscribe(e => { - if (navigation.hasOwnProperty(e)) { - navigation[e](); - } + ngAfterViewInit(){ + let isBuild = false; + this.authenticationService.watchUser.subscribe(user => { + this.user = user; + this.roomService.getRoomByShortId(this.encodedShortId).subscribe(e => { + this.room = e; + if (isBuild){ + return; + } + this.initNavigation(); + isBuild = true; + }); + }); + } + + initNavigation(){ + const list: ComponentRef<any>[] = this.composeService.builder(this.headerService.getHost(), e => { + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'flag', + text:'header.edit-session-description', + callback:() => this.editSessionDescription(), + condition:() => this.user.role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'visibility_off', + isSVGIcon:false, + text:'header.moderation-mode', + callback:() => this.showCommentsDialog(), + condition:() => this.user.role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'gavel', + text:'header.edit-moderator', + callback:() => this.showModeratorsDialog(), + condition:() => this.user.role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'comment_tag', + isSVGIcon:true, + text:'header.edit-tags', + callback:() => this.showTagsDialog(), + condition:() => this.user.role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'grade', + iconColor:Palette.YELLOW, + text:'header.bonustoken', + callback:() => this.showBonusTokenDialog(), + condition:() => this.user.role >= UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'file_download', + text:'header.export-questions', + callback:() => this.exportQuestions(), + condition:() => this.user.role >= UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'clear', + text:'header.profanity-filter', + callback:() => this.toggleProfanityFilter(), + condition:() => this.user.role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'delete_sweep', + iconColor:Palette.RED, + text:'header.delete-questions', + callback:() => this.openDeleteRoomDialog(), + condition:() => this.user.role === UserRole.CREATOR + }); + e.menuItem({ + translate:this.headerService.getTranslate(), + icon:'delete', + iconColor:Palette.RED, + isSVGIcon:false, + text:'header.delete-room', + callback:() => this.openDeleteRoomDialog(), + condition:() => this.user.role === UserRole.CREATOR + }); + e.altToggle( + { + translate:this.headerService.getTranslate(), + text:'header.unlock', + icon:'block', + iconColor:Palette.RED, + color:Palette.RED + }, + { + translate:this.headerService.getTranslate(), + text:'header.block', + icon:'block', + iconColor:Palette.RED + }, + ArsObserver.build<boolean>(e => { + e.set(this.room.questionsBlocked); + e.onChange(a => { + this.room.questionsBlocked = a.get(); + this.roomService.updateRoom(this.room).subscribe(); + if (a.get()){ + this.headerService.getTranslate().get('header.questions-blocked').subscribe(msg => { + this.headerService.getNotificationService().show(msg); + }); + } + }); + }) + , + () => this.user.role > UserRole.PARTICIPANT + ); + }); + this.onDestroyListener.subscribe(() => { + list.forEach(e => e.destroy()); }); } @@ -99,9 +212,9 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni toggleProfanityFilter(){ const dialogRef = this.dialog.open(ProfanitySettingsComponent, { - width: '400px' + width:'400px' }); - dialogRef.componentInstance.editRoom=this.room; + dialogRef.componentInstance.editRoom = this.room; } editSessionName() { @@ -116,10 +229,10 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni editSessionDescription(){ const dialogRef = this.dialog.open(RoomDescriptionSettingsComponent, { - width: '900px', - maxWidth: 'calc( 100% - 50px )', - maxHeight: 'calc( 100vh - 50px )', - autoFocus: false + width:'900px', + maxWidth:'calc( 100% - 50px )', + maxHeight:'calc( 100vh - 50px )', + autoFocus:false }); dialogRef.componentInstance.editRoom = this.room; } @@ -137,12 +250,12 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni deleteQuestions(){ const dialogRef = this.dialog.open(DeleteCommentsComponent, { - width: '400px' + width:'400px' }); dialogRef.componentInstance.roomId = this.room.id; dialogRef.afterClosed() .subscribe(result => { - if (result === 'delete') { + if (result === 'delete'){ this.translateService.get('room-page.comments-deleted').subscribe(msg => { this.notificationService.show(msg); }); @@ -151,23 +264,23 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni }); } - ngOnDestroy() { + ngOnDestroy(){ super.ngOnDestroy(); this.commentCounterEmitSubscription.unsubscribe(); this.titleService.resetTitle(); - if (this.headerInterface) { + if (this.headerInterface){ this.headerInterface.unsubscribe(); } + this.onDestroyListener.emit(); } - ngAfterContentInit(): void { - setTimeout( () => { + ngAfterContentInit(): void{ + setTimeout(() => { document.getElementById('live_announcer-button').focus(); }, 700); } - ngOnInit() { - this.initNavigation(); + ngOnInit(){ window.scroll(0, 0); this.translateService.use(localStorage.getItem('currentLang')); this.route.params.subscribe(params => { @@ -176,43 +289,43 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni }); this.listenerFn = this._r.listen(document, 'keyup', (event) => { const lang: string = this.translateService.currentLang; - if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit1) === true && this.eventService.focusOnInput === false) { + if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit1) === true && this.eventService.focusOnInput === false){ document.getElementById('question_answer-button').focus(); - } else if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit3) === true && this.eventService.focusOnInput === false) { + }else if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit3) === true && this.eventService.focusOnInput === false){ document.getElementById('gavel-button').focus(); - } else if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit4) === true && this.eventService.focusOnInput === false) { + }else if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit4) === true && this.eventService.focusOnInput === false){ document.getElementById('settings-menu').focus(); - } else if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit8) === true && this.eventService.focusOnInput === false) { + }else if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit8) === true && this.eventService.focusOnInput === false){ this.liveAnnouncer.clear(); - if (lang === 'de') { - this.liveAnnouncer.announce('Aktueller Sitzungs-Name: ' + this.room.name + '. ' + - 'Aktueller Raum-Code: ' + this.room.shortId); - } else { - this.liveAnnouncer.announce('Current Session-Name: ' + this.room.name + '. ' + - 'Current Session Code: ' + this.room.shortId); -} - } else if ( + if (lang === 'de'){ + this.liveAnnouncer.announce('Aktueller Sitzungs-Name: ' + this.room.name + '. ' + + 'Aktueller Raum-Code: ' + this.room.shortId); + }else{ + this.liveAnnouncer.announce('Current Session-Name: ' + this.room.name + '. ' + + 'Current Session Code: ' + this.room.shortId); + } + }else if ( KeyboardUtils.isKeyEvent(event, KeyboardKey.Digit9, KeyboardKey.Escape) === true && this.eventService.focusOnInput === false - ) { + ){ this.announce(); - } else if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Escape) === true && this.eventService.focusOnInput === true) { + }else if (KeyboardUtils.isKeyEvent(event, KeyboardKey.Escape) === true && this.eventService.focusOnInput === true){ this.eventService.makeFocusOnInputFalse(); } }); } - public announce() { + public announce(){ const lang: string = this.translateService.currentLang; this.liveAnnouncer.clear(); - if (lang === 'de') { + if (lang === 'de'){ this.liveAnnouncer.announce('Du befindest dich in der von dir erstellten Sitzung. ' + 'Drücke die Taste 1 um auf die Fragen-Übersicht zu gelangen, ' + 'die Taste 2 um das Sitzungs-Menü zu öffnen, die Taste 3 um in die Moderationsübersicht zu gelangen, ' + 'die Taste 4 um Einstellungen an der Sitzung vorzunehmen, ' + 'die Taste 8 um den aktuellen Raum-Code zu hören, die Taste 0 um auf den Zurück-Button zu gelangen, ' + 'oder die Taste 9 um diese Ansage zu wiederholen.', 'assertive'); - } else { + }else{ this.liveAnnouncer.announce('You are in the session you created. ' + 'Press key 1 to go to the question overview, ' + 'Press key 2 to open the session menu, key 3 to go to the moderation overview, ' + @@ -222,8 +335,8 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni } } - postRoomLoadHook() { - if (this.moderationEnabled) { + postRoomLoadHook(){ + if (this.moderationEnabled){ this.viewModuleCount = this.viewModuleCount + 1; this.commentService.countByRoomId(this.room.id, false).subscribe(commentCounter => { this.moderatorCommentCounter = commentCounter; @@ -232,12 +345,12 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni } - updateCommentSettings(settings: CommentSettingsDialog) { + updateCommentSettings(settings: CommentSettingsDialog){ this.room.tags = settings.tags; - if (this.moderationEnabled && !settings.enableModeration) { + if (this.moderationEnabled && !settings.enableModeration){ this.viewModuleCount = this.viewModuleCount - 1; - } else if (!this.moderationEnabled && settings.enableModeration) { + }else if (!this.moderationEnabled && settings.enableModeration){ this.viewModuleCount = this.viewModuleCount + 1; } @@ -245,16 +358,16 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni localStorage.setItem('moderationEnabled', String(this.moderationEnabled)); } - resetThreshold(): void { + resetThreshold(): void{ this.room.moderated = undefined; this.room.threshold = undefined; this.room.directSend = undefined; this.room.tags = undefined; } - saveChanges() { + saveChanges(){ this.roomService.updateRoom(this.updRoom) - .subscribe((room) => { + .subscribe((room) => { this.room = room; this.translateService.get('room-page.changes-successful').subscribe(msg => { this.notification.show(msg); @@ -267,104 +380,104 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni }); } - showSettingsDialog(): void { + showSettingsDialog(): void{ this.updRoom = JSON.parse(JSON.stringify(this.room)); const dialogRef = this.dialog.open(RoomEditComponent, { - width: '400px' + width:'400px' }); dialogRef.componentInstance.editRoom = this.updRoom; dialogRef.afterClosed() - .subscribe(result => { - if (result === 'abort') { - return; - } else if (result !== 'delete') { - this.saveChanges(); - } - }); - dialogRef.backdropClick().subscribe( res => { - dialogRef.close('abort'); + .subscribe(result => { + if (result === 'abort'){ + return; + }else if (result !== 'delete'){ + this.saveChanges(); + } + }); + dialogRef.backdropClick().subscribe(res => { + dialogRef.close('abort'); }); } - showCommentsDialog(): void { + showCommentsDialog(): void{ this.updRoom = JSON.parse(JSON.stringify(this.room)); const dialogRef = this.dialog.open(CommentSettingsComponent, { - width: '400px' + width:'400px' }); dialogRef.componentInstance.roomId = this.room.id; dialogRef.componentInstance.editRoom = this.updRoom; dialogRef.afterClosed() - .subscribe(result => { - if (result === 'abort') { - return; - } else { - if (result instanceof CommentSettingsDialog) { - this.updateCommentSettings(result); - this.saveChanges(); - } + .subscribe(result => { + if (result === 'abort'){ + return; + }else{ + if (result instanceof CommentSettingsDialog){ + this.updateCommentSettings(result); + this.saveChanges(); } - }); - dialogRef.backdropClick().subscribe( res => { + } + }); + dialogRef.backdropClick().subscribe(res => { dialogRef.close('abort'); }); } - showModeratorsDialog(): void { + showModeratorsDialog(): void{ const dialogRef = this.dialog.open(ModeratorsComponent, { - width: '400px' + width:'400px' }); dialogRef.componentInstance.roomId = this.room.id; } - showBonusTokenDialog(): void { + showBonusTokenDialog(): void{ const dialogRef = this.dialog.open(BonusTokenComponent, { - width: '400px' + width:'400px' }); dialogRef.componentInstance.room = this.room; } - showTagCloud(): void { + showTagCloud(): void{ const dialogRef = this.dialog.open(TopicCloudFilterComponent, { - width: '400px' + width:'400px' }); } - showTagsDialog(): void { + showTagsDialog(): void{ this.updRoom = JSON.parse(JSON.stringify(this.room)); const dialogRef = this.dialog.open(TagsComponent, { - width: '400px' + width:'400px' }); let tags = []; - if (this.room.tags !== undefined) { + if (this.room.tags !== undefined){ tags = this.room.tags; } dialogRef.componentInstance.tags = tags; dialogRef.afterClosed() - .subscribe(result => { - if (!result || result === 'abort') { - return; - } else { - this.updRoom.tags = result; - this.saveChanges(); - } - }); + .subscribe(result => { + if (!result || result === 'abort'){ + return; + }else{ + this.updRoom.tags = result; + this.saveChanges(); + } + }); } - openDeleteRoomDialog(): void { + openDeleteRoomDialog(): void{ const dialogRef = this.dialog.open(RoomDeleteComponent, { - width: '400px' + width:'400px' }); dialogRef.componentInstance.room = this.room; dialogRef.afterClosed() .subscribe(result => { - if (result === 'delete') { + if (result === 'delete'){ this.deleteRoom(); } }); } - deleteRoom(): void { + deleteRoom(): void{ this.translationService.get('room-page.deleted').subscribe(msg => { this.notificationService.show(this.room.name + msg); }); @@ -375,7 +488,7 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni }); } - copyShortId(): void { + copyShortId(): void{ const selBox = document.createElement('textarea'); selBox.style.position = 'fixed'; selBox.style.left = '0'; @@ -388,7 +501,7 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni document.execCommand('copy'); document.body.removeChild(selBox); this.translateService.get('room-page.session-id-copied').subscribe(msg => { - this.notification.show(msg, '', { duration: 2000 }); + this.notification.show(msg, '', {duration:2000}); }); } } diff --git a/src/app/components/participant/room-participant-page/room-participant-page.component.ts b/src/app/components/participant/room-participant-page/room-participant-page.component.ts index 2d98c36882c0ee0423214693e4e97cb5426466f7..3e4dce340b68f63eb6bfddc28500fddd8c70ffc7 100644 --- a/src/app/components/participant/room-participant-page/room-participant-page.component.ts +++ b/src/app/components/participant/room-participant-page/room-participant-page.component.ts @@ -46,7 +46,7 @@ export class RoomParticipantPageComponent extends RoomPageComponent implements O } ngAfterContentInit(): void { - setTimeout( () => { + setTimeout(() => { document.getElementById('live_announcer-button').focus(); }, 700); } @@ -85,15 +85,15 @@ export class RoomParticipantPageComponent extends RoomPageComponent implements O 'oder die Taste 9 um diese Ansage zu wiederholen.', 'assertive'); } else { this.liveAnnouncer.announce('You have entered the session' + this.room.name + 'with the room code' + this.room.shortId - + '.' + 'Press 0 to go back to the previous page, ' + + + '.' + 'Press 0 to go back to the previous page, ' + '1 to ask a question, 2 for the session menu' + - '8 to hear the current sesion code or 9 to repeat this announcement.'); + '8 to hear the current sesion code or 9 to repeat this announcement.'); } } preRoomLoadHook(): Observable<any> { - this.authenticationService.watchUser.subscribe( user => this.user = user); + this.authenticationService.watchUser.subscribe(user => this.user = user); if (!this.user) { return this.authenticationService.guestLogin(UserRole.PARTICIPANT).pipe(map((user) => { return user; @@ -104,8 +104,10 @@ export class RoomParticipantPageComponent extends RoomPageComponent implements O } postRoomLoadHook() { - this.authenticationService.setAccess(this.room.shortId, UserRole.PARTICIPANT); + if (!this.authenticationService.hasAccess(this.room.shortId, UserRole.PARTICIPANT)) { + this.authenticationService.setAccess(this.room.shortId, UserRole.PARTICIPANT); + this.roomService.addToHistory(this.room.id); + } this.authenticationService.checkAccess(this.room.shortId); - this.roomService.addToHistory(this.room.id); } } diff --git a/src/app/components/shared/comment-list/comment-list.component.ts b/src/app/components/shared/comment-list/comment-list.component.ts index 73792a426156e98b7a09510a38a47324c654f1b7..123708a47319432801d5dd366631f2c45baca790 100644 --- a/src/app/components/shared/comment-list/comment-list.component.ts +++ b/src/app/components/shared/comment-list/comment-list.component.ts @@ -228,7 +228,7 @@ export class CommentListComponent implements OnInit, OnDestroy { this.authenticationService.watchUser.subscribe(newUser => { if (newUser) { this.user = newUser; - if (this.userRole === 0) { + if (this.userRole === UserRole.PARTICIPANT) { this.voteService.getByRoomIdAndUserID(this.roomId, this.user.id).subscribe(votes => { for (const v of votes) { this.commentVoteMap.set(v.commentId, v); @@ -252,12 +252,12 @@ export class CommentListComponent implements OnInit, OnDestroy { this.roomId = this.room.id; this.moderationEnabled = this.room.moderated; this.directSend = this.room.directSend; - this.commentsEnabled = (this.userRole > 0) || !this.room.questionsBlocked; + this.commentsEnabled = (this.userRole > UserRole.PARTICIPANT) || !this.room.questionsBlocked; } }); this.moderationEnabled = this.room.moderated; this.directSend = this.room.directSend; - this.commentsEnabled = (this.userRole > 0) || !this.room.questionsBlocked; + this.commentsEnabled = (this.userRole > UserRole.PARTICIPANT) || !this.room.questionsBlocked; this.createCommentWrapper = new CreateCommentWrapper(this.translateService, this.notificationService, this.commentService, this.dialog, this.room); localStorage.setItem('moderationEnabled', JSON.stringify(this.moderationEnabled)); @@ -281,11 +281,6 @@ export class CommentListComponent implements OnInit, OnDestroy { }); this.subscribeCommentStream(); }); - /** - if (this.userRole === UserRole.PARTICIPANT) { - this.openCreateDialog(); - } - */ }); }); }); diff --git a/src/app/components/shared/header/header.component.html b/src/app/components/shared/header/header.component.html index 137553fb86ed6690beaf4b1a42eea215a56dcaba..f864a02026a522293c8260b57fd02762e195ebad 100644 --- a/src/app/components/shared/header/header.component.html +++ b/src/app/components/shared/header/header.component.html @@ -112,15 +112,9 @@ [overlapTrigger]="false"> <!-- <p class="mat-menu-inner-title">Account</p>--> - <div class="mat-menu-inner-box"> - - <!-- Home --> - <ng-container *ngIf="router.url.endsWith('/home')"> - </ng-container> + <ng-container arsComposeHost></ng-container> - <!-- Session list --> - <ng-container *ngIf="router.url.endsWith('/user')"> - </ng-container> + <div class="mat-menu-inner-box"> <!-- Room --> <ng-container *ngIf="router.url.includes('/room/')"> @@ -221,45 +215,14 @@ </button> </ng-container> - <ng-container *ngIf="router.url.includes('/participant/room/')"> - </ng-container> - <ng-container *ngIf="router.url.includes('/moderator/room/')"> - </ng-container> - <ng-container *ngIf="router.url.includes('/creator/room/')"> - </ng-container> </ng-container> <!-- Session --> <ng-container *ngIf="!router.url.endsWith('/comments') && !router.url.includes('/comment/') && !router.url.endsWith('tagcloud')"> - <!-- app-room-participant-page --> - <ng-container *ngIf="router.url.includes('/participant/room/')"> - </ng-container> - <!-- app-room-creator-page --> - <ng-container *ngIf="router.url.includes('/moderator/room/')"> - </ng-container> - <!-- app-room-creator-page --> - <ng-container *ngIf="router.url.includes('/creator/room/')"> - - <button mat-menu-item - (click)="navigateEditSessionDescription()" - tabindex="0"> - <mat-icon>flag</mat-icon> - <span>{{'header.edit-session-description' | translate}}</span> - </button> - - <button mat-menu-item - (click)="navigateModeratorSettings()" - *ngIf="user" - tabindex="0"> - <mat-icon class="gavel">visibility_off</mat-icon> - <span>{{'header.moderation-mode' | translate}}</span> - </button> - - </ng-container> </ng-container> @@ -268,41 +231,6 @@ <ng-container *ngIf="user && user.role == 3 && !router.url.includes('/participant') && !router.url.endsWith('/comments') && !router.url.includes('/comment/') && !router.url.endsWith('tagcloud')"> - <button mat-menu-item - (click)="navigateModerator()" - tabindex="0"> - <mat-icon>gavel</mat-icon> - <span>{{'header.edit-moderator' | translate}}</span> - </button> - - <button mat-menu-item - (click)="navigateTags()" - tabindex="0"> - <mat-icon svgIcon="comment_tag"></mat-icon> - <span>{{'header.edit-tags' | translate}}</span> - </button> - - <button mat-menu-item - *ngIf="user" - (click)="navigateRoomBonusToken()" - tabindex="0"> - <mat-icon class="star">grade</mat-icon> - <span>{{'header.bonustoken' | translate}}</span> - </button> - - <button mat-menu-item - (click)="navigateExportQuestions()" - tabindex="0"> - <mat-icon>file_download</mat-icon> - <span>{{'header.export-questions' | translate}}</span> - </button> - - <button mat-menu-item - (click)="navigateProfanityFilter()" - tabindex="0"> - <mat-icon>clear</mat-icon> - <span>{{'header.profanity-filter' | translate}}</span> - </button> </ng-container> @@ -352,33 +280,6 @@ <span>{{'header.export-questions' | translate}}</span> </button> - <button mat-menu-item - tabindex="0" - *ngIf="user && user.role == 3 && !router.url.includes('/participant') && !router.url.endsWith('/comments') && !router.url.includes('/comment/') && !router.url.endsWith('tagcloud')" - (click)="blockQuestions()" - [ngClass]="{'color-warn': room && room.questionsBlocked}" - > - <mat-icon class="color-warn">block</mat-icon> - <span *ngIf="room && !room.questionsBlocked">{{'header.block' | translate}}</span> - <span *ngIf="room && room.questionsBlocked">{{'header.unlock' | translate}}</span> - </button> - - <button mat-menu-item - *ngIf="user && user.role == 3 && !router.url.includes('/participant') && !router.url.endsWith('/comments') && !router.url.includes('/comment/') && !router.url.endsWith('tagcloud')" - (click)="navigateDeleteQuestions()" - tabindex="0"> - <mat-icon class="color-warn">delete_sweep</mat-icon> - <span>{{'header.delete-questions' | translate}}</span> - </button> - - <button mat-menu-item - *ngIf="user && user.role == 3 && !router.url.includes('/participant') && !router.url.endsWith('/comments') && !router.url.includes('/comment/') && !router.url.endsWith('tagcloud')" - (click)="navigateDeleteRoom()" - tabindex="0"> - <mat-icon class="color-warn">delete</mat-icon> - <span>{{'header.delete-room' | translate}}</span> - </button> - </ng-container> </div> diff --git a/src/app/components/shared/header/header.component.ts b/src/app/components/shared/header/header.component.ts index 4a424a57c5283fddd906a0e0c4cd5a08fe3f3cec..91b73f7049fc062374e482fc0e988f4358871089 100644 --- a/src/app/components/shared/header/header.component.ts +++ b/src/app/components/shared/header/header.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Renderer2 } from '@angular/core'; +import { AfterViewInit, Component, OnInit, Renderer2, ViewChild } from '@angular/core'; import { AuthenticationService } from '../../../services/http/authentication.service'; import { NotificationService } from '../../../services/util/notification.service'; import { NavigationEnd, Router } from '@angular/router'; @@ -29,13 +29,14 @@ import { TopicCloudAdminService } from '../../../services/util/topic-cloud-admin import { HeaderService } from '../../../services/util/header.service'; import { OnboardingService } from '../../../services/util/onboarding.service'; import { WorkerConfigDialogComponent } from '../_dialogs/worker-config-dialog/worker-config-dialog.component'; +import { ArsComposeHostDirective } from '../../../../../projects/ars/src/lib/compose/ars-compose-host.directive'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.scss'] }) -export class HeaderComponent implements OnInit { +export class HeaderComponent implements OnInit,AfterViewInit { user: User; cTime: string; shortId: string; @@ -51,12 +52,13 @@ export class HeaderComponent implements OnInit { userActivity = 0; deviceType = localStorage.getItem('deviceType'); private _subscriptionRoomService = null; + @ViewChild(ArsComposeHostDirective)host:ArsComposeHostDirective; constructor(public location: Location, private authenticationService: AuthenticationService, - private notificationService: NotificationService, + public notificationService: NotificationService, public router: Router, - private translationService: TranslateService, + public translationService: TranslateService, public dialog: MatDialog, private userService: UserService, public eventService: EventService, @@ -72,6 +74,10 @@ export class HeaderComponent implements OnInit { ) { } + ngAfterViewInit(){ + this.headerService.initHeader(()=>this); + } + ngOnInit() { this.topicCloudAdminService.getAdminData.subscribe(data => { this.isAdminConfigEnabled = !TopicCloudAdminService.isTopicRequirementDisabled(data); diff --git a/src/app/components/shared/room-list/room-list.component.ts b/src/app/components/shared/room-list/room-list.component.ts index d958998532dad4fe12b0a5188e53b6549537abdd..f727755e59ab4dc465055399421f3f45e00a4254 100644 --- a/src/app/components/shared/room-list/room-list.component.ts +++ b/src/app/components/shared/room-list/room-list.component.ts @@ -15,7 +15,6 @@ import { NotificationService } from '../../../services/util/notification.service import { TranslateService } from '@ngx-translate/core'; import { RemoveFromHistoryComponent } from '../_dialogs/remove-from-history/remove-from-history.component'; import { MatTableDataSource } from '@angular/material/table'; -import { ExportCsv } from '../../../models/export-csv'; import { BonusTokenService } from '../../../services/http/bonus-token.service'; import { Export } from '../../../models/export'; diff --git a/src/app/services/util/header.service.ts b/src/app/services/util/header.service.ts index 6dca0ebe44b103c60c1173e5a39731e96bd4c613..a3bf73fdc42291668573924661cd8fa243af14aa 100644 --- a/src/app/services/util/header.service.ts +++ b/src/app/services/util/header.service.ts @@ -1,4 +1,8 @@ import {Injectable} from '@angular/core'; +import { HeaderComponent } from '../../components/shared/header/header.component'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationService } from './notification.service'; +import { ArsComposeHostDirective } from '../../../../projects/ars/src/lib/compose/ars-compose-host.directive'; @Injectable({ providedIn: 'root' @@ -9,10 +13,14 @@ export class HeaderService { private userActivityListener:((v:number)=>void)[]=[]; private userActivityToggle:boolean; private userActivityToggleListener:((v:boolean)=>void)[]=[]; - + private headerComponent:()=>HeaderComponent; constructor() {} + public initHeader(headerComponent:()=>HeaderComponent){ + this.headerComponent=headerComponent; + } + public setCurrentUserActivity(e:number){ if(this.userActivity!=e){ this.userActivity=e; @@ -37,4 +45,16 @@ export class HeaderService { this.userActivityToggleListener.push(f); } + public getTranslate():TranslateService{ + return this.headerComponent().translationService; + } + + public getNotificationService():NotificationService{ + return this.headerComponent().notificationService; + } + + public getHost():ArsComposeHostDirective{ + return this.headerComponent().host; + } + } diff --git a/src/theme/Theme.ts b/src/theme/Theme.ts index 95617b81292df7d575a1d3d960271f459eb59694..8681f149e9c90037fd09d7790374d27e73800ce3 100644 --- a/src/theme/Theme.ts +++ b/src/theme/Theme.ts @@ -1,4 +1,8 @@ +export class Palette { + public static RED:string='var(--red)'; + public static YELLOW:string='var(--yellow)'; +} export class ColorElem {