MOBILE-4283 quiz: Allow students to hide timer
parent
670bb10330
commit
573af7c513
|
@ -27,15 +27,15 @@
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
core-timer .core-timer {
|
core-timer {
|
||||||
&.core-timer-under-300 {
|
&.core-timer-under-300 {
|
||||||
background-color: var(--danger-tint);
|
--timer-background: var(--danger-tint);
|
||||||
color: var(--danger-shade);
|
--timer-text-color: var(--danger-shade);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.core-timer-under-900 {
|
&.core-timer-under-900 {
|
||||||
background-color: var(--warning-tint);
|
--timer-background: var(--warning-tint);
|
||||||
color: var(--warning-shade);
|
--timer-text-color: var(--warning-shade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<!-- @todo plagiarism_print_disclosure -->
|
<!-- @todo plagiarism_print_disclosure -->
|
||||||
<core-timer *ngIf="timeLimitEndTime > 0" [endTime]="timeLimitEndTime" (finished)="timeUp()" timeUpText="00:00:00"
|
<core-timer *ngIf="timeLimitEndTime > 0" [endTime]="timeLimitEndTime" (finished)="timeUp()" timeUpText="00:00:00"
|
||||||
[timerText]="'addon.mod_assign.assigntimeleft' | translate" [align]="'center'" [timeLeftClassThreshold]="-1"
|
[timerText]="'addon.mod_assign.assigntimeleft' | translate" [align]="'center'" [timeLeftClassThreshold]="-1"
|
||||||
[underTimeClassThresholds]="[300, 900]" class="ion-margin-horizontal">
|
[underTimeClassThresholds]="[300, 900]">
|
||||||
</core-timer>
|
</core-timer>
|
||||||
|
|
||||||
<!-- Assign activity instructions and attachments if needed. -->
|
<!-- Assign activity instructions and attachments if needed. -->
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
:host ::ng-deep {
|
:host {
|
||||||
core-timer {
|
core-timer {
|
||||||
display: block;
|
&.core-timer-under-300 {
|
||||||
|
--timer-background: var(--danger-tint);
|
||||||
|
--timer-text-color: var(--danger-shade);
|
||||||
|
}
|
||||||
|
|
||||||
.core-timer {
|
&.core-timer-under-900 {
|
||||||
&.core-timer-under-300 {
|
--timer-background: var(--warning-tint);
|
||||||
background-color: var(--danger-tint);
|
--timer-text-color: var(--warning-shade);
|
||||||
color: var(--danger-shade);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.core-timer-under-900 {
|
|
||||||
background-color: var(--warning-tint);
|
|
||||||
color: var(--warning-shade);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,9 @@
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
<!-- Navigation arrows and time left. -->
|
<core-timer *ngIf="loaded && endTime && questions.length && !quizAborted && !showSummary" [endTime]="endTime" (finished)="timeUp()"
|
||||||
<ion-toolbar *ngIf="loaded && endTime && questions.length && !quizAborted && !showSummary" color="light">
|
[timerText]="'addon.mod_quiz.timeleft' | translate" [hiddable]="true">
|
||||||
<ion-title>
|
</core-timer>
|
||||||
<core-timer [endTime]="endTime" (finished)="timeUp()" [timerText]="'addon.mod_quiz.timeleft' | translate" [align]="'center'">
|
|
||||||
</core-timer>
|
|
||||||
</ion-title>
|
|
||||||
</ion-toolbar>
|
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content class="limited-width">
|
<ion-content class="limited-width">
|
||||||
<core-loading [hideUntil]="loaded" class="has-spacer">
|
<core-loading [hideUntil]="loaded" class="has-spacer">
|
||||||
|
|
|
@ -9,22 +9,14 @@ $quiz-timer-iterations: 15 !default;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-content ion-toolbar {
|
core-timer {
|
||||||
border-bottom: 1px solid var(--stroke);
|
// Make the timer go red when it's reaching 0.
|
||||||
}
|
@for $i from 0 through $quiz-timer-iterations {
|
||||||
|
&.core-timer-timeleft-#{$i} {
|
||||||
core-timer ::ng-deep {
|
$timer-background: rgba($quiz-timer-warn-color, 1 - ($i / $quiz-timer-iterations));
|
||||||
.core-timer {
|
--timer-background: #{$timer-background};
|
||||||
// Make the timer go red when it's reaching 0.
|
@if $i <= $quiz-timer-iterations / 2 {
|
||||||
@for $i from 0 through $quiz-timer-iterations {
|
--timer-text-color: var(--white);
|
||||||
&.core-timer-timeleft-#{$i} {
|
|
||||||
background-color: rgba($quiz-timer-warn-color, 1 - ($i / $quiz-timer-iterations)) !important;
|
|
||||||
|
|
||||||
@if $i <= $quiz-timer-iterations / 2 {
|
|
||||||
label, span, ion-icon {
|
|
||||||
color: var(--white);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,32 @@
|
||||||
<ion-item *ngIf="mode !== modeBasic" class="core-timer" role="timer"
|
<ng-container *ngIf="timeLeft !== undefined">
|
||||||
[ngClass]="{'ion-text-center': align == 'center', 'ion-text-end': align == 'right'}">
|
<ion-item *ngIf="mode !== modeBasic" class="core-timer" role="timer" [attr.aria-label]="timerText">
|
||||||
<ion-icon name="fas-clock" slot="start" aria-hidden="true"></ion-icon>
|
<ion-label class="ion-justify-content-{{align}}">
|
||||||
<ion-label>
|
<ng-container *ngTemplateOutlet="timerTemplate"></ng-container>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<div *ngIf="mode === modeBasic" class="core-timer ion-padding ion-justify-content-{{align}}" role="timer" [attr.aria-label]="timerText">
|
||||||
<ng-container *ngTemplateOutlet="timerTemplate"></ng-container>
|
<ng-container *ngTemplateOutlet="timerTemplate"></ng-container>
|
||||||
</ion-label>
|
</div>
|
||||||
</ion-item>
|
|
||||||
|
|
||||||
<div *ngIf="mode === modeBasic" class="core-timer ion-padding" role="timer"
|
<ng-template #timerTemplate>
|
||||||
[ngClass]="{'ion-text-center': align == 'center', 'ion-text-end': align == 'right'}">
|
<ng-container *ngIf="timeLeft > 0">
|
||||||
<ng-container *ngTemplateOutlet="timerTemplate"></ng-container>
|
<div class="ion-text-wrap">
|
||||||
</div>
|
<span *ngIf="timerText" class="core-timer-text">{{ timerText }}</span>
|
||||||
|
<span *ngIf="showTimeLeft" class="core-timer-time-left">
|
||||||
|
{{ timeLeft | coreSecondsToHMS }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ng-template #timerTemplate>
|
<div *ngIf="hiddable" class="core-timer-visibility">
|
||||||
<span *ngIf="timerText" class="core-timer-text">{{ timerText }}</span>
|
( <button class="as-link" *ngIf="showTimeLeft" (click)="toggleTimeLeftVisibility()">{{ 'core.hide' | translate }}</button>
|
||||||
<span *ngIf="timeLeft && timeLeft > 0" class="core-timer-time-left">{{ timeLeft | coreSecondsToHMS }}</span>
|
<button class="as-link" *ngIf="!showTimeLeft" (click)="toggleTimeLeftVisibility()">{{ 'core.show' | translate }}</button> )
|
||||||
<span class="core-timesup" *ngIf="timeLeft !== undefined && timeLeft <= 0">
|
</div>
|
||||||
<ng-container *ngIf="timeUpText">{{ timeUpText }}</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="!timeUpText">{{ 'core.timesup' | translate }}</ng-container>
|
|
||||||
</span>
|
<div class="core-timesup" *ngIf="timeLeft <= 0">
|
||||||
</ng-template>
|
<ng-container *ngIf="timeUpText">{{ timeUpText }}</ng-container>
|
||||||
|
<ng-container *ngIf="!timeUpText">{{ 'core.timesup' | translate }}</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</ng-container>
|
||||||
|
|
|
@ -1,14 +1,40 @@
|
||||||
:host {
|
:host {
|
||||||
.core-timer {
|
--timer-background: transparent;
|
||||||
--background: transparent !important;
|
|
||||||
border-radius: var(--big-radius);
|
|
||||||
|
|
||||||
|
.core-timer {
|
||||||
.core-timer-time-left, .core-timesup {
|
.core-timer-time-left, .core-timesup {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
.core-timesup {
|
||||||
margin-right: 5px;
|
flex-grow: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-item.core-timer {
|
||||||
|
--background: var(--timer-background);
|
||||||
|
--color: var(--timer-text-color, var(--text-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
div.core-timer {
|
||||||
|
background: var(--timer-background);
|
||||||
|
color: var(--timer-text-color, var(--text-color));
|
||||||
|
border-radius: var(--big-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: var(--timer-text-color, var(--core-link-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-item.core-timer ion-label,
|
||||||
|
div.core-timer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.core-timer-visibility {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,12 @@ import { CoreTimeUtils } from '@services/utils/time';
|
||||||
})
|
})
|
||||||
export class CoreTimerComponent implements OnInit, OnDestroy {
|
export class CoreTimerComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@Input() endTime?: string | number; // Timestamp (in seconds) when the timer should end.
|
@Input() endTime = 0; // Timestamp (in seconds) when the timer should end.
|
||||||
@Input() timerText?: string; // Text to show next to the timer. If not defined, no text shown.
|
@Input() timerText?: string; // Text to show next to the timer. If not defined, no text shown.
|
||||||
@Input() timeLeftClass?: string; // Name of the class to apply with each second. By default, 'core-timer-timeleft-'.
|
@Input() timeLeftClass?: string; // Name of the class to apply with each second. By default, 'core-timer-timeleft-'.
|
||||||
@Input() timeLeftClassThreshold = 100; // Number of seconds to start adding the timeLeftClass. Set it to -1 to not add it.
|
@Input() timeLeftClassThreshold = 100; // Number of seconds to start adding the timeLeftClass. Set it to -1 to not add it.
|
||||||
@Input() align?: string; // Where to align the time and text. Defaults to 'left'. Other values: 'center', 'right'.
|
@Input() align = 'start'; // Where to align the time and text. Defaults to 'start'. Other values: 'center', 'end'.
|
||||||
|
@Input() hiddable = false; // Whether the user can hide the time left.
|
||||||
@Input() timeUpText?: string; // Text to show when the timer reaches 0. If not defined, 'core.timesup'.
|
@Input() timeUpText?: string; // Text to show when the timer reaches 0. If not defined, 'core.timesup'.
|
||||||
@Input() mode: CoreTimerMode = CoreTimerMode.ITEM; // How to display data.
|
@Input() mode: CoreTimerMode = CoreTimerMode.ITEM; // How to display data.
|
||||||
@Input() underTimeClassThresholds = []; // Number of seconds to add the class 'core-timer-under-'.
|
@Input() underTimeClassThresholds = []; // Number of seconds to add the class 'core-timer-under-'.
|
||||||
|
@ -41,6 +42,7 @@ export class CoreTimerComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
timeLeft?: number; // Seconds left to end.
|
timeLeft?: number; // Seconds left to end.
|
||||||
modeBasic = CoreTimerMode.BASIC;
|
modeBasic = CoreTimerMode.BASIC;
|
||||||
|
showTimeLeft = true;
|
||||||
|
|
||||||
protected timeInterval?: number;
|
protected timeInterval?: number;
|
||||||
protected element?: HTMLElement;
|
protected element?: HTMLElement;
|
||||||
|
@ -54,18 +56,21 @@ export class CoreTimerComponent implements OnInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
const timeLeftClass = this.timeLeftClass || 'core-timer-timeleft-';
|
const timeLeftClass = this.timeLeftClass || 'core-timer-timeleft-';
|
||||||
const endTime = Math.round(Number(this.endTime));
|
const endTime = Math.round(this.endTime);
|
||||||
this.underTimeClassThresholds.sort((a, b) => a - b); // Sort by increase order.
|
this.underTimeClassThresholds.sort((a, b) => a - b); // Sort by increase order.
|
||||||
|
|
||||||
if (!endTime) {
|
// @deprecated since 4.3. Use start/end instead.
|
||||||
return;
|
if (this.align === 'left') {
|
||||||
|
this.align = 'start';
|
||||||
|
} else if (this.align === 'right') {
|
||||||
|
this.align = 'end';
|
||||||
}
|
}
|
||||||
|
|
||||||
let container: HTMLElement | undefined;
|
let container: HTMLElement | undefined;
|
||||||
|
|
||||||
// Check time left every 200ms.
|
// Check time left every 200ms.
|
||||||
this.timeInterval = window.setInterval(() => {
|
this.timeInterval = window.setInterval(() => {
|
||||||
container = container || this.elementRef.nativeElement.querySelector('.core-timer');
|
container = container || this.elementRef.nativeElement;
|
||||||
this.timeLeft = Math.max(endTime - CoreTimeUtils.timestamp(), 0);
|
this.timeLeft = Math.max(endTime - CoreTimeUtils.timestamp(), 0);
|
||||||
|
|
||||||
if (container) {
|
if (container) {
|
||||||
|
@ -104,7 +109,14 @@ export class CoreTimerComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component destroyed.
|
* Toggles the time left visibility.
|
||||||
|
*/
|
||||||
|
toggleTimeLeftVisibility(): void {
|
||||||
|
this.showTimeLeft = !this.showTimeLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
clearInterval(this.timeInterval);
|
clearInterval(this.timeInterval);
|
||||||
|
|
Loading…
Reference in New Issue