Checks, if content is too large and hide it in 'meta-text-block'

- Also updates the library 'css-element-queries'.
- Builds a directive to check, if the scroll-height of an element
has changed.
This commit is contained in:
GabrielMeyer 2019-08-21 15:05:03 +02:00
parent a05a81b1b2
commit c49ea53ab1
12 changed files with 307 additions and 50 deletions

View File

@ -51,7 +51,7 @@
"@tinymce/tinymce-angular": "^3.2.0", "@tinymce/tinymce-angular": "^3.2.0",
"acorn": "^6.1.1", "acorn": "^6.1.1",
"core-js": "^3.0.1", "core-js": "^3.0.1",
"css-element-queries": "^1.1.1", "css-element-queries": "^1.2.1",
"exceljs": "1.10.0", "exceljs": "1.10.0",
"file-saver": "^2.0.1", "file-saver": "^2.0.1",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",

View File

@ -3,18 +3,29 @@
<mat-card-header> <mat-card-header>
<mat-card-title> <mat-card-title>
<div class="title-container"> <div class="title-container">
<div> <div class="ellipsis-overflow">
<ng-container *ngTemplateOutlet="title"></ng-container> <ng-container *ngTemplateOutlet="title"></ng-container>
</div> </div>
<div *ngIf="showActionRow"> <div class="action-row" *ngIf="showActionRow">
<ng-container *ngTemplateOutlet="actionRow"></ng-container> <ng-container *ngTemplateOutlet="actionRow"></ng-container>
</div> </div>
</div> </div>
</mat-card-title> </mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<!-- Content -->
<div
#contentBox
[osHeightResizing]="resizeSubject"
class="content"
[ngClass]="!isExpanded && !disableExpandControl ? 'collapsed' : ''"
>
<ng-container *ngTemplateOutlet="content"></ng-container> <ng-container *ngTemplateOutlet="content"></ng-container>
</div>
<!-- Expanding control -->
<div class="small show-entire-text" *ngIf="canExpand && !disableExpandControl">
<a (click)="isExpanded = !isExpanded">{{ isExpanded ? 'Hide more text' : 'Show full text' }}</a>
</div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</div> </div>

View File

@ -1,11 +1,20 @@
@import '~@angular/material/theming';
@mixin os-meta-text-block-style($theme) {
$foreground: map-get($theme, foreground);
.meta-text-block { .meta-text-block {
padding: 0px; padding: 0px;
margin: 20px 0; margin: 20px 0;
min-width: 200px; min-width: 200px;
.mat-icon-button mat-icon {
color: mat-color($foreground, icon);
font-size: 18px;
}
mat-card-header { mat-card-header {
display: inherit; display: inherit;
padding-top: 10px;
margin: 0; margin: 0;
.mat-card-header-text { .mat-card-header-text {
@ -18,7 +27,12 @@
.title-container { .title-container {
display: flex; display: flex;
align-items: center;
justify-content: space-between; justify-content: space-between;
.action-row {
display: flex;
}
} }
} }
} }
@ -26,5 +40,33 @@
mat-card-content { mat-card-content {
padding: 15px; padding: 15px;
word-wrap: break-word; word-wrap: break-word;
.content {
transition: all 1s ease;
&.collapsed {
max-height: 200px;
overflow: hidden;
position: relative;
&::after {
content: '';
position: absolute;
top: 150px;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
}
}
}
.show-entire-text {
text-align: end;
margin-top: 4px;
a {
cursor: pointer;
}
}
}
} }
} }

View File

@ -1,7 +1,8 @@
import { Component, Input } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Subject, Subscription } from 'rxjs';
import { ViewportService } from 'app/core/ui-services/viewport.service'; import { ViewportService } from 'app/core/ui-services/viewport.service';
import { BaseComponent } from '../../../base.component'; import { BaseComponent } from '../../../base.component';
@ -14,11 +15,102 @@ import { BaseComponent } from '../../../base.component';
templateUrl: './meta-text-block.component.html', templateUrl: './meta-text-block.component.html',
styleUrls: ['./meta-text-block.component.scss'] styleUrls: ['./meta-text-block.component.scss']
}) })
export class MetaTextBlockComponent extends BaseComponent { export class MetaTextBlockComponent extends BaseComponent implements OnInit, OnDestroy {
/**
* Indicates, whether the action-row should be shown.
*/
@Input() @Input()
public showActionRow: boolean; public showActionRow: boolean;
public constructor(title: Title, translate: TranslateService, public vp: ViewportService) { /**
* Indicates, whether the content should be expandable or always expanded.
*
* If `true`, it resets the flag `isExpanded`. This prevents an error -
* when the given element is expanded and the control was disabled, the
* subscription is deleted.
*/
@Input()
public set disableExpandControl(disableControl: boolean) {
this._disableExpandControl = disableControl;
if (disableControl) {
this.isExpanded = false;
this.cd.detectChanges();
}
}
/**
* Returns the flag `disableExpandControl`.
*/
public get disableExpandControl(): boolean {
return this._disableExpandControl;
}
/**
* Boolean, whether the control to expand the element should be disabled or not.
*/
private _disableExpandControl = false;
/**
* Boolean to see, if the content can be expanded.
*/
public canExpand = false;
/**
* Boolean to see, if the content is currently expanded.
*/
public isExpanded = false;
/**
* Subject to listen, whether the height of the given dom-element has changed.
*/
public resizeSubject = new Subject<number>();
/**
* Subscription to resize-change-events for the height of the content.
*/
private contentSubscription: Subscription;
/**
* Default constructor.
*
* @param title
* @param translate
* @param vp
* @param cd
*/
public constructor(
title: Title,
translate: TranslateService,
public vp: ViewportService,
private cd: ChangeDetectorRef
) {
super(title, translate); super(title, translate);
} }
/**
* Sets the subscription.
*/
public ngOnInit(): void {
this.contentSubscription = this.resizeSubject.subscribe(newHeight => this.resizesContentBox(newHeight));
}
/**
* Deletes and unsubscribes to subscription.
*/
public ngOnDestroy(): void {
if (this.contentSubscription) {
this.contentSubscription.unsubscribe();
this.contentSubscription = null;
}
}
/**
* Function to check, if the new height of the element
* is greater than the limit of `200px`.
*
* @param height The new height as `number` of the linked element.
*/
private resizesContentBox(height: number): void {
this.canExpand = height > 200;
}
} }

View File

@ -0,0 +1,8 @@
import { HeightResizingDirective } from './height-resizing.directive';
describe('HeightResizingDirective', () => {
it('should create an instance', () => {
const directive = new HeightResizingDirective(null);
expect(directive).toBeTruthy();
});
});

View File

@ -0,0 +1,69 @@
import { Directive, ElementRef, Input, OnInit } from '@angular/core';
import { ResizeSensor } from 'css-element-queries';
import { Subject } from 'rxjs';
/**
* Directive to check, if the `ScrollHeight` of the underlying element has changed.
*/
@Directive({
selector: '[osHeightResizing]'
})
export class HeightResizingDirective implements OnInit {
/**
* A subject to notify, when the given element changes its `ScrollHeight`.
*/
@Input()
public osHeightResizing: Subject<number>;
/**
* The underlying native-element of the passed element.
*/
private nativeElement: HTMLElement;
/**
* Stores the old height to see, if the height changed.
*/
private oldHeight: number;
/**
* Constructor.
* Initializes the `nativeElement`.
*
* @param element The passed element for this directive.
*/
public constructor(element: ElementRef) {
if (element) {
this.nativeElement = <HTMLElement>element.nativeElement;
}
}
/**
* Initializes the listener for resizing events of the passed element.
*/
public ngOnInit(): void {
// tslint:disable-next-line:no-unused-expression
new ResizeSensor(this.nativeElement, () => {
this.checkElementForChanges();
});
this.checkElementForChanges();
}
/**
* Function to check, if the height of the passed element changed
* and if the new height is different to the old one.
*
* If the new height is different to the old one, the subject gets a new value.
*/
private checkElementForChanges(): void {
if (this.nativeElement.scrollHeight === this.oldHeight) {
return;
}
this.oldHeight = this.nativeElement.scrollHeight;
if (this.osHeightResizing) {
this.osHeightResizing.next(this.nativeElement.scrollHeight);
}
}
}

View File

@ -102,6 +102,7 @@ import { OverlayComponent } from 'app/site/common/components/overlay/overlay.com
import { PreviewComponent } from './components/preview/preview.component'; import { PreviewComponent } from './components/preview/preview.component';
import { PdfViewerModule } from 'ng2-pdf-viewer'; import { PdfViewerModule } from 'ng2-pdf-viewer';
import { GlobalSpinnerComponent } from 'app/site/common/components/global-spinner/global-spinner.component'; import { GlobalSpinnerComponent } from 'app/site/common/components/global-spinner/global-spinner.component';
import { HeightResizingDirective } from './directives/height-resizing.directive';
/** /**
* Share Module for all "dumb" components and pipes. * Share Module for all "dumb" components and pipes.
@ -223,6 +224,7 @@ import { GlobalSpinnerComponent } from 'app/site/common/components/global-spinne
ProjectorButtonComponent, ProjectorButtonComponent,
ProjectionDialogComponent, ProjectionDialogComponent,
ResizedDirective, ResizedDirective,
HeightResizingDirective,
MetaTextBlockComponent, MetaTextBlockComponent,
ProjectorComponent, ProjectorComponent,
SlideContainerComponent, SlideContainerComponent,
@ -290,7 +292,8 @@ import { GlobalSpinnerComponent } from 'app/site/common/components/global-spinne
GlobalSpinnerComponent, GlobalSpinnerComponent,
SuperSearchComponent, SuperSearchComponent,
OverlayComponent, OverlayComponent,
PreviewComponent PreviewComponent,
HeightResizingDirective
], ],
providers: [ providers: [
{ provide: DateAdapter, useClass: OpenSlidesDateAdapter }, { provide: DateAdapter, useClass: OpenSlidesDateAdapter },

View File

@ -2,6 +2,7 @@
<os-meta-text-block <os-meta-text-block
*ngIf="sectionVisible(section)" *ngIf="sectionVisible(section)"
[showActionRow]="canEditSection(section) || comments[section.id].comment" [showActionRow]="canEditSection(section) || comments[section.id].comment"
[disableExpandControl]="isCommentEdited(section)"
icon="comment" icon="comment"
> >
<ng-container class="meta-text-block-title"> <ng-container class="meta-text-block-title">

View File

@ -1,4 +1,4 @@
<os-meta-text-block showActionRow="true"> <os-meta-text-block showActionRow="true" [disableExpandControl]="true">
<ng-container class="meta-text-block-title"> <ng-container class="meta-text-block-title">
<span translate>Voting result</span> <span translate>Voting result</span>
<span *ngIf="pollIndex">&nbsp;({{ pollIndex + 1 }})</span> <span *ngIf="pollIndex">&nbsp;({{ pollIndex + 1 }})</span>

View File

@ -1,4 +1,4 @@
<os-meta-text-block showActionRow="true" icon="speaker_notes"> <os-meta-text-block showActionRow="true" icon="speaker_notes" [disableExpandControl]="true">
<!-- Title row --> <!-- Title row -->
<ng-container class="meta-text-block-title"> <ng-container class="meta-text-block-title">
<span translate>Personal note</span> <span translate>Personal note</span>

View File

@ -1,4 +1,4 @@
<os-meta-text-block showActionRow="false" *ngIf="projector"> <os-meta-text-block showActionRow="false" *ngIf="projector" [disableExpandControl]="true">
<ng-container class="meta-text-block-title"> <ng-container class="meta-text-block-title">
{{ projector.getTitle() | translate }} {{ projector.getTitle() | translate }}
</ng-container> </ng-container>
@ -35,7 +35,11 @@
<h3 translate>Resolution and size</h3> <h3 translate>Resolution and size</h3>
<!-- Aspect ratio field --> <!-- Aspect ratio field -->
<mat-radio-group formControlName="aspectRatio" [name]="projector.id"> <mat-radio-group formControlName="aspectRatio" [name]="projector.id">
<mat-radio-button *ngFor="let ratio of aspectRatiosKeys" [value]="ratio" (change)="aspectRatioChanged($event)"> <mat-radio-button
*ngFor="let ratio of aspectRatiosKeys"
[value]="ratio"
(change)="aspectRatioChanged($event)"
>
{{ ratio }} {{ ratio }}
</mat-radio-button> </mat-radio-button>
</mat-radio-group> </mat-radio-group>
@ -53,7 +57,11 @@
<!-- projection defaults --> <!-- projection defaults -->
<h3 translate>Projection defaults</h3> <h3 translate>Projection defaults</h3>
<mat-select formControlName="projectiondefaults_id" placeholder="{{ 'Projection defaults' | translate }}" [multiple]="true"> <mat-select
formControlName="projectiondefaults_id"
placeholder="{{ 'Projection defaults' | translate }}"
[multiple]="true"
>
<mat-option *ngFor="let pd of projectionDefaults" [value]="pd.id"> <mat-option *ngFor="let pd of projectionDefaults" [value]="pd.id">
{{ pd.getTitle() | translate }} {{ pd.getTitle() | translate }}
</mat-option> </mat-option>
@ -71,7 +79,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="reset-button"> <div class="reset-button">
<button mat-icon-button matTooltip="{{ 'Reset' | translate }}" (click)="resetField('color', '#000000')"> <button
mat-icon-button
matTooltip="{{ 'Reset' | translate }}"
(click)="resetField('color', '#000000')"
>
<mat-icon>replay</mat-icon> <mat-icon>replay</mat-icon>
</button> </button>
</div> </div>
@ -88,7 +100,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="reset-button"> <div class="reset-button">
<button mat-icon-button matTooltip="{{ 'Reset' | translate }}" (click)="resetField('background_color', '#ffffff')"> <button
mat-icon-button
matTooltip="{{ 'Reset' | translate }}"
(click)="resetField('background_color', '#ffffff')"
>
<mat-icon>replay</mat-icon> <mat-icon>replay</mat-icon>
</button> </button>
</div> </div>
@ -105,7 +121,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="reset-button"> <div class="reset-button">
<button mat-icon-button matTooltip="{{ 'Reset' | translate }}" (click)="resetField('header_background_color', '#317796')"> <button
mat-icon-button
matTooltip="{{ 'Reset' | translate }}"
(click)="resetField('header_background_color', '#317796')"
>
<mat-icon>replay</mat-icon> <mat-icon>replay</mat-icon>
</button> </button>
</div> </div>
@ -122,7 +142,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="reset-button"> <div class="reset-button">
<button mat-icon-button matTooltip="{{ 'Reset' | translate }}" (click)="resetField('header_font_color', '#f5f5f5')"> <button
mat-icon-button
matTooltip="{{ 'Reset' | translate }}"
(click)="resetField('header_font_color', '#f5f5f5')"
>
<mat-icon>replay</mat-icon> <mat-icon>replay</mat-icon>
</button> </button>
</div> </div>
@ -139,7 +163,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="reset-button"> <div class="reset-button">
<button mat-icon-button matTooltip="{{ 'Reset' | translate }}" (click)="resetField('header_h1_color', '#317796')"> <button
mat-icon-button
matTooltip="{{ 'Reset' | translate }}"
(click)="resetField('header_h1_color', '#317796')"
>
<mat-icon>replay</mat-icon> <mat-icon>replay</mat-icon>
</button> </button>
</div> </div>
@ -156,7 +184,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="reset-button"> <div class="reset-button">
<button mat-icon-button matTooltip="{{ 'Reset' | translate }}" (click)="resetField('chyron_background_color', '#317796')"> <button
mat-icon-button
matTooltip="{{ 'Reset' | translate }}"
(click)="resetField('chyron_background_color', '#317796')"
>
<mat-icon>replay</mat-icon> <mat-icon>replay</mat-icon>
</button> </button>
</div> </div>
@ -173,7 +205,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="reset-button"> <div class="reset-button">
<button mat-icon-button matTooltip="{{ 'Reset' | translate }}" (click)="resetField('chyron_font_color', '#ffffff')"> <button
mat-icon-button
matTooltip="{{ 'Reset' | translate }}"
(click)="resetField('chyron_font_color', '#ffffff')"
>
<mat-icon>replay</mat-icon> <mat-icon>replay</mat-icon>
</button> </button>
</div> </div>

View File

@ -23,6 +23,7 @@
@import './app/site/mediafiles/components/mediafile-list/mediafile-list.component.scss-theme.scss'; @import './app/site/mediafiles/components/mediafile-list/mediafile-list.component.scss-theme.scss';
@import './app/site/common/components/super-search/super-search.component.scss'; @import './app/site/common/components/super-search/super-search.component.scss';
@import './app/shared/components/rounded-input/rounded-input.component.scss'; @import './app/shared/components/rounded-input/rounded-input.component.scss';
@import './app/shared/components/meta-text-block/meta-text-block.component.scss';
/** fonts */ /** fonts */
@import './assets/styles/fonts.scss'; @import './assets/styles/fonts.scss';
@ -46,6 +47,7 @@ $narrow-spacing: (
@include os-mediafile-list-theme($theme); @include os-mediafile-list-theme($theme);
@include os-super-search-style($theme); @include os-super-search-style($theme);
@include os-rounded-input-style($theme); @include os-rounded-input-style($theme);
@include os-meta-text-block-style($theme);
} }
/** Load projector specific SCSS values */ /** Load projector specific SCSS values */
@ -546,13 +548,6 @@ button.mat-menu-item.selected {
margin-right: 8px !important; margin-right: 8px !important;
} }
.meta-text-block .mat-icon-button {
margin-top: -12px !important;
}
.meta-text-block .mat-icon-button mat-icon {
font-size: 18px;
}
/** helper classes for margin/padding */ /** helper classes for margin/padding */
.spacer-top-3 { .spacer-top-3 {
margin-top: 3px !important; margin-top: 3px !important;