Partially scale and scroll the motion slide

This commit is contained in:
FinnStutzenstein 2019-05-27 15:30:07 +02:00 committed by Emanuel Schütze
parent 318533cf68
commit 1f7ace6b4b
6 changed files with 153 additions and 16 deletions

View File

@ -7,7 +7,7 @@
position: absolute; position: absolute;
left: 50px; left: 50px;
top: 50px; top: 50px;
line-height: 1.5em; line-height: 1.5;
} }
} }

View File

@ -8,6 +8,7 @@ import { BaseSlideComponent } from 'app/slides/base-slide-component';
import { SlideData } from 'app/core/core-services/projector-data.service'; import { SlideData } from 'app/core/core-services/projector-data.service';
import { ProjectorElement } from 'app/shared/models/core/projector'; import { ProjectorElement } from 'app/shared/models/core/projector';
import { ViewProjector } from 'app/site/projector/models/view-projector'; import { ViewProjector } from 'app/site/projector/models/view-projector';
import { isBaseScaleScrollSlideComponent } from 'app/slides/base-scale-scroll-slide-component';
function hasError(obj: object): obj is { error: string } { function hasError(obj: object): obj is { error: string } {
return (<{ error: string }>obj).error !== undefined; return (<{ error: string }>obj).error !== undefined;
@ -28,6 +29,13 @@ export class SlideContainerComponent extends BaseComponent {
private slide: ViewContainerRef; private slide: ViewContainerRef;
private slideRef: ComponentRef<BaseSlideComponent<object>>; private slideRef: ComponentRef<BaseSlideComponent<object>>;
/**
* A slide is autonomic, if it takes care of scaling and scrolling by itself.
*/
private get slideIsAutonomic(): boolean {
return !!this.slideRef && !!this.slideRef.instance && isBaseScaleScrollSlideComponent(this.slideRef.instance);
}
/** /**
* The data for this slide. Will be accessed below. * The data for this slide. Will be accessed below.
*/ */
@ -59,8 +67,9 @@ export class SlideContainerComponent extends BaseComponent {
if (this.previousSlideName !== slideData.element.name) { if (this.previousSlideName !== slideData.element.name) {
this.slideChanged(slideData.element); this.slideChanged(slideData.element);
this.previousSlideName = slideData.element.name; this.previousSlideName = slideData.element.name;
} else {
this.setDataForComponent();
} }
this.setDataForComponent();
} }
public get slideData(): SlideData<object> { public get slideData(): SlideData<object> {
@ -77,6 +86,7 @@ export class SlideContainerComponent extends BaseComponent {
this._projector = projector; this._projector = projector;
this.setProjectorForComponent(); this.setProjectorForComponent();
this.updateScroll(); this.updateScroll();
this.updateScale();
} }
public get projector(): ViewProjector { public get projector(): ViewProjector {
@ -101,18 +111,19 @@ export class SlideContainerComponent extends BaseComponent {
return this._scroll; return this._scroll;
} }
private _scale: number;
/** /**
* Update the slideStyle, when the scale changes. * Update the slideStyle, when the scale changes.
*/ */
@Input() @Input()
public set scale(value: number) { public set scale(value: number) {
if (this.slideOptions.scaleable) { this._scale = value;
value *= 10; this.updateScale();
value += 100; }
this.slideStyle['font-size'] = `${value}%`;
} else { public get scale(): number {
this.slideStyle['font-size'] = '100%'; return this._scale;
}
} }
/** /**
@ -133,10 +144,11 @@ export class SlideContainerComponent extends BaseComponent {
} }
/** /**
* Updates the 'margin-top' attribute in the slide styles. * Updates the 'margin-top' attribute in the slide styles. Propages the sroll to
* autonomic slides.
*/ */
private updateScroll(): void { private updateScroll(): void {
if (this.slideOptions.scrollable) { if (this.slideOptions.scrollable && !this.slideIsAutonomic) {
let value = this.scroll; let value = this.scroll;
value *= -100; value *= -100;
if (this.projector.show_header_footer) { if (this.projector.show_header_footer) {
@ -145,6 +157,28 @@ export class SlideContainerComponent extends BaseComponent {
this.slideStyle['margin-top'] = `${value}px`; this.slideStyle['margin-top'] = `${value}px`;
} else { } else {
this.slideStyle['margin-top'] = '0px'; this.slideStyle['margin-top'] = '0px';
if (this.slideIsAutonomic && isBaseScaleScrollSlideComponent(this.slideRef.instance)) {
this.slideRef.instance.scroll = this.scroll;
}
}
}
/**
* Updates the 'font-size' style attributes. Propagates the scale to autonomic slides.
*/
private updateScale(): void {
if (this.slideOptions.scaleable && !this.slideIsAutonomic) {
let scale = this.scale;
scale *= 10;
scale += 100;
this.slideStyle['font-size'] = `${scale}%`;
} else {
this.slideStyle['font-size'] = '100%';
if (this.slideIsAutonomic && isBaseScaleScrollSlideComponent(this.slideRef.instance)) {
this.slideRef.instance.scale = this.scale;
}
} }
} }
@ -170,6 +204,8 @@ export class SlideContainerComponent extends BaseComponent {
this.slideRef = this.slide.createComponent(slideFactory); this.slideRef = this.slide.createComponent(slideFactory);
this.setDataForComponent(); this.setDataForComponent();
this.setProjectorForComponent(); this.setProjectorForComponent();
this.updateScale();
this.updateScroll();
}); });
} }

View File

@ -0,0 +1,32 @@
import { Input } from '@angular/core';
import { BaseSlideComponent } from './base-slide-component';
export function isBaseScaleScrollSlideComponent<T extends object>(obj: any): obj is IBaseScaleScrollSlideComponent<T> {
return !!obj && obj.scroll !== undefined && obj.scale !== undefined;
}
/**
* A description of BaseScaleScrollSlideComponent. Usefull for "multi"-inheritance.
*/
export interface IBaseScaleScrollSlideComponent<T extends object> extends BaseSlideComponent<T> {
scroll: number;
scale: number;
}
/**
* A base slide component, which is autonomic with respect to scaling and srolling, meaning
* that the slide itself (and not the slide container) will take care of this.
*/
export abstract class BaseScaleScrollSlideComponent<T extends object> extends BaseSlideComponent<T>
implements IBaseScaleScrollSlideComponent<T> {
@Input()
public scroll: number;
@Input()
public scale: number;
public constructor() {
super();
}
}

View File

@ -1,5 +1,5 @@
<div *ngIf="data"> <div *ngIf="data">
<div id="sidebox" *ngIf="data.data.show_meta_box"> <div id="sidebox" *ngIf="data.data.show_meta_box" [ngStyle]="{'margin-top': projector.show_header_footer ? '144px' : '94px'}">
<!-- Submitters --> <!-- Submitters -->
<h3 translate>Submitters</h3> <h3 translate>Submitters</h3>
<span *ngFor="let submitter of data.data.submitter; let last = last"> <span *ngFor="let submitter of data.data.submitter; let last = last">
@ -13,13 +13,17 @@
</div> </div>
</div> </div>
<div [ngStyle]="{'width': data.data.show_meta_box ? 'calc(100% - 250px)' : '100%'}"> <div [ngStyle]="{width: data.data.show_meta_box ? 'calc(100% - 250px)' : '100%'}">
<!-- Title --> <!-- Title -->
<div class="spacer" [ngStyle]="{height: projector.show_header_footer ? '50px' : '0'}"></div>
<div class="slidetitle"> <div class="slidetitle">
<h1>{{ data.data.title }}</h1> <h1>{{ data.data.title }}</h1>
<h2><span translate>Motion</span> {{ data.data.identifier }}</h2> <h2><span translate>Motion</span> {{ data.data.identifier }}</h2>
</div> </div>
</div>
<div id="text-wrapper">
<div id="text" [ngStyle]="textDivStyles">
<!-- Text --> <!-- Text -->
<span class="text-prefix-label">{{ preamble | translate }}</span> <span class="text-prefix-label">{{ preamble | translate }}</span>
@ -73,4 +77,5 @@
<div [innerHTML]="data.data.reason"></div> <div [innerHTML]="data.data.reason"></div>
</div> </div>
</div> </div>
</div>
</div> </div>

View File

@ -4,10 +4,22 @@
opacity: 0.5; opacity: 0.5;
} }
/**
* Override some special values which will be set dynamically
*/
.slidetitle {
/* Original: 40px: This is done in the `scroll()` method, so the
* motion text will be cut (to be taken by word) on the grey line */
margin-bottom: 0px !important;
h1 {
margin: 0 !important;
}
}
#sidebox { #sidebox {
width: 260px; width: 260px;
right: 0; right: 0;
margin-top: 94px;
background: #d3d3d3; background: #d3d3d3;
border-radius: 7px 0 0 7px; border-radius: 7px 0 0 7px;
padding: 3px 7px 10px 10px; padding: 3px 7px 10px 10px;
@ -21,13 +33,25 @@
} }
} }
.spacer {
min-width: 1px;
}
#text-wrapper {
overflow: hidden;
}
#text {
position: relative;
}
/* override the absolute position of outside linenumbers on motion slide */ /* override the absolute position of outside linenumbers on motion slide */
:host ::ng-deep .motion-text { :host ::ng-deep .motion-text {
&.line-numbers-outside { &.line-numbers-outside {
.os-line-number { .os-line-number {
&:after { &:after {
top: 19px; top: 19px;
font-size: 13px; font-size: 15px;
} }
} }
} }

View File

@ -13,13 +13,15 @@ import { SlideData } from '../../../core/core-services/projector-data.service';
import { MotionSlideObjAmendmentParagraph } from './motion-slide-obj-amendment-paragraph'; import { MotionSlideObjAmendmentParagraph } from './motion-slide-obj-amendment-paragraph';
import { BaseMotionSlideComponent } from '../base/base-motion-slide'; import { BaseMotionSlideComponent } from '../base/base-motion-slide';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { IBaseScaleScrollSlideComponent } from 'app/slides/base-scale-scroll-slide-component';
@Component({ @Component({
selector: 'os-motion-slide', selector: 'os-motion-slide',
templateUrl: './motion-slide.component.html', templateUrl: './motion-slide.component.html',
styleUrls: ['./motion-slide.component.scss'] styleUrls: ['./motion-slide.component.scss']
}) })
export class MotionSlideComponent extends BaseMotionSlideComponent<MotionSlideData> { export class MotionSlideComponent extends BaseMotionSlideComponent<MotionSlideData>
implements IBaseScaleScrollSlideComponent<MotionSlideData> {
/** /**
* Indicates the LineNumberingMode Mode. * Indicates the LineNumberingMode Mode.
*/ */
@ -61,6 +63,8 @@ export class MotionSlideComponent extends BaseMotionSlideComponent<MotionSlideDa
this.preamble = value.data.preamble; this.preamble = value.data.preamble;
this.crMode = value.element.mode || 'original'; this.crMode = value.element.mode || 'original';
this.textDivStyles.width = value.data.show_meta_box ? 'calc(100% - 250px)' : '100%';
this.recalcUnifiedChanges(); this.recalcUnifiedChanges();
} }
@ -68,6 +72,42 @@ export class MotionSlideComponent extends BaseMotionSlideComponent<MotionSlideDa
return this._data; return this._data;
} }
private _scroll = 0;
@Input()
public set scroll(value: number) {
this._scroll = value;
value *= -100;
value += 40;
this.textDivStyles['margin-top'] = `${value}px`;
}
public get scroll(): number {
return this._scroll;
}
private _scale = 0;
@Input()
public set scale(value: number) {
this._scale = value;
value *= 10;
value += 100;
this.textDivStyles['font-size'] = `${value}%`;
}
public get scale(): number {
return this._scale;
}
public textDivStyles: {
width?: string;
'margin-top'?: string;
'font-size'?: string;
} = {};
public constructor( public constructor(
translate: TranslateService, translate: TranslateService,
motionRepo: MotionRepositoryService, motionRepo: MotionRepositoryService,