MOBILE-2302 courses: Implement course progress component
parent
296932b76a
commit
e778448824
|
@ -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 {}
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}}%</div>
|
||||
<div class="progress-indicator">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<title>{{course.progress}}%</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>
|
|
@ -0,0 +1,2 @@
|
|||
core-courses-course-progress {
|
||||
}
|
|
@ -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});
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue