MOBILE-2302 courses: Implement course progress component

main
Dani Palou 2017-12-11 14:59:44 +01:00
parent 296932b76a
commit e778448824
9 changed files with 247 additions and 7 deletions

View File

@ -21,6 +21,7 @@ import { CoreMarkRequiredComponent } from './mark-required/mark-required';
import { CoreInputErrorsComponent } from './input-errors/input-errors';
import { CoreShowPasswordComponent } from './show-password/show-password';
import { CoreIframeComponent } from './iframe/iframe';
import { CoreProgressBarComponent } from './progress-bar/progress-bar';
@NgModule({
declarations: [
@ -28,7 +29,8 @@ import { CoreIframeComponent } from './iframe/iframe';
CoreMarkRequiredComponent,
CoreInputErrorsComponent,
CoreShowPasswordComponent,
CoreIframeComponent
CoreIframeComponent,
CoreProgressBarComponent
],
imports: [
IonicModule,
@ -40,7 +42,8 @@ import { CoreIframeComponent } from './iframe/iframe';
CoreMarkRequiredComponent,
CoreInputErrorsComponent,
CoreShowPasswordComponent,
CoreIframeComponent
CoreIframeComponent,
CoreProgressBarComponent
]
})
export class CoreComponentsModule {}

View File

@ -0,0 +1,8 @@
<div *ngIf="progress >= 0">
<progress max="100" [value]="progress">
<div class="progress-bar-fallback" role="progressbar" aria-valuemin="0" aria-valuemax="100" [attr.aria-valuenow]="progress">
<span [style.width]="width"></span>
</div>
</progress>
<span class="mm-progress-text">{{ 'core.percentagenumber' | translate: {$a: text} }}</span>
</div>

View File

@ -0,0 +1,53 @@
$mm-progress-bar-height: 5px !default;
core-progress-bar {
padding-right: 55px;
position: relative;
display: block;
// @extend .clearfix;
.mm-progress-text {
margin-left: 10px;
line-height: 35px;
color: $gray-darker;
right: 0;
top: 0;
color: #626262;
position: absolute;
}
progress {
-webkit-appearance: none;
appearance: none;
height: $mm-progress-bar-height;
margin: 15px 0;
padding: 0;
display: block;
width: 100%;
.progress-bar-fallback,
&[value]::-webkit-progress-bar {
background-color: $gray-light;
border-radius: 2px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1) inset;
}
.progress-bar-fallback span,
&[value]::-webkit-progress-value {
background-color: $mm-color-light;
border-radius: 2px;
}
.progress-bar-fallback {
width: 100%;
height: $mm-progress-bar-height;
display: block;
position: relative;
span {
height: $mm-progress-bar-height;
display: block;
}
}
}
}

View File

@ -0,0 +1,60 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, OnChanges, SimpleChange, ChangeDetectionStrategy } from '@angular/core';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
/**
* Component to show a progress bar and its value.
*
* Example usage:
* <core-progress-bar [progress]="percentage"></core-progress-bar>
*/
@Component({
selector: 'core-progress-bar',
templateUrl: 'progress-bar.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CoreProgressBarComponent implements OnChanges {
@Input() progress: number|string; // Percentage from 0 to 100.
@Input() text?: string; // Percentage in text to be shown at the right. If not defined, progress will be used.
width: SafeStyle;
protected textSupplied = false;
constructor(private sanitizer: DomSanitizer) {}
/**
* Detect changes on input properties.
*/
ngOnChanges(changes: {[name: string]: SimpleChange}) {
if (changes.text && typeof changes.text.currentValue != 'undefined') {
// User provided a custom text, don't use default.
this.textSupplied = true;
}
if (changes.progress) {
// Progress has changed.
this.width = this.sanitizer.bypassSecurityTrustStyle(this.progress + '%');
if (typeof this.progress == 'string') {
this.progress = parseInt(this.progress, 10);
}
if (this.progress < 0 || isNaN(this.progress)) {
this.progress = -1;
} else if (!this.textSupplied) {
this.text = String(this.progress);
}
}
}
}

View File

@ -0,0 +1,35 @@
<ion-card>
<a ion-item text-wrap class="item-course" [class.item-progress]="roundProgress" (click)="openCourse(course)" [title]="course.fullname">
<div class="progress-chart-container" *ngIf="roundProgress">
<div *ngIf="course.progress !== false" class="progress-doughnut">
<div class="progress-text has-percent">{{course.progress}}&#37;</div>
<div class="progress-indicator">
<svg xmlns="http://www.w3.org/2000/svg">
<g>
<title>{{course.progress}}&#37;</title>
<circle class="circle percent-{{course.progress}}" r="27.5" cx="35" cy="35"/>
</g>
</svg>
</div>
</div>
<div *ngIf="course.progress === false" class="no-progress">
<ion-icon name="ionic"></ion-icon>
</div>
</div>
<h2><core-format-text [text]="course.fullname"></core-format-text></h2>
<core-progress-bar *ngIf="!roundProgress && course.progress !== false" [progress]="course.progress"></core-progress-bar>
<!-- Course options. -->
<!-- <i *ngIf="actionsLoaded" class="icon ion-android-more-vertical mm-animate-show-hide" ng-click="showCourseActions($event)"></i>
<ion-spinner *ngIf="!actionsLoaded" class="mm-animate-show-hide"></ion-spinner> -->
<!-- Download course spinner. -->
<!-- <ion-spinner *ngIf="prefetchCourseIcon == 'spinner'" class="mm-course-download-spinner"></ion-spinner> -->
</a>
<ion-item text-wrap *ngIf="showSummary && course.summary">
<p>
<summary>
<core-format-text [text]="course.summary" maxHeight="60"></core-format-text>
</summary>
</p>
</ion-item>
<ng-content></ng-content>
</ion-card>

View File

@ -0,0 +1,2 @@
core-courses-course-progress {
}

View File

@ -0,0 +1,72 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, OnInit } from '@angular/core';
import { NavController } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreUtilsProvider } from '../../../../providers/utils/utils';
/**
* This component is meant to display a course for a list of courses with progress.
*
* Example usage:
*
* <core-courses-course-progress *ngFor="let course of filteredCourses" [course]="course" showSummary="true">
* </core-courses-course-progress>
*/
@Component({
selector: 'core-courses-course-progress',
templateUrl: 'course-progress.html'
})
export class CoreCoursesCourseProgressComponent implements OnInit {
@Input() course: any; // The course to render.
@Input() roundProgress?: boolean|string; // Whether to show the progress.
@Input() showSummary?: boolean|string; // Whether to show the summary.
actionsLoaded = true;
prefetchCourseIcon: string;
protected obsStatus;
protected downloadText;
protected downloadingText;
protected downloadButton = {
isDownload: true,
className: 'mm-download-course',
priority: 1000
};
protected buttons;
constructor(private navCtrl: NavController, private translate: TranslateService, private utils: CoreUtilsProvider) {
this.downloadText = this.translate.instant('core.course.downloadcourse');
this.downloadingText = this.translate.instant('core.downloading');
}
/**
* Component being initialized.
*/
ngOnInit() {
// @todo: Handle course prefetch.
// @todo: Handle course handlers (participants, etc.).
this.roundProgress = this.utils.isTrueOrOne(this.roundProgress);
this.showSummary = this.utils.isTrueOrOne(this.showSummary);
}
/**
* Open a course.
*/
openCourse(course) {
this.navCtrl.push('CoreCourseSectionPage', {course: course});
}
}

View File

@ -14,14 +14,19 @@
import { NgModule } from '@angular/core';
import { CoreCoursesProvider } from './providers/courses';
import { CoreCoursesCourseProgressComponent } from './components/course-progress/course-progress';
@NgModule({
declarations: [
CoreCoursesCourseProgressComponent
],
imports: [
],
providers: [
CoreCoursesProvider
],
exports: [
CoreCoursesCourseProgressComponent
]
})
export class CoreCoursesModule {}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Directive, ElementRef, Input, OnInit, Output, EventEmitter } from '@angular/core';
import { Directive, ElementRef, Input, Output, EventEmitter, OnChanges, SimpleChange } from '@angular/core';
import { Platform } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreAppProvider } from '../providers/app';
@ -38,7 +38,7 @@ import { CoreExternalContentDirective } from '../directives/external-content';
@Directive({
selector: 'core-format-text'
})
export class CoreFormatTextDirective implements OnInit {
export class CoreFormatTextDirective implements OnChanges {
@Input() text: string; // The text to format.
@Input() siteId?: string; // Site ID to use.
@Input() component?: string; // Component for CoreExternalContentDirective.
@ -68,10 +68,12 @@ export class CoreFormatTextDirective implements OnInit {
}
/**
* Function executed when the directive is initialized.
* Detect changes on input properties.
*/
ngOnInit() : void {
this.formatAndRenderContents();
ngOnChanges(changes: {[name: string]: SimpleChange}) {
if (changes.text) {
this.formatAndRenderContents();
}
}
/**