MOBILE-3659 course: Implement section selector
parent
6d4e37431a
commit
01e9e20014
|
@ -23,6 +23,7 @@ import { CoreCourseFormatComponent } from './format/format';
|
|||
import { CoreCourseModuleComponent } from './module/module';
|
||||
import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion';
|
||||
import { CoreCourseModuleDescriptionComponent } from './module-description/module-description';
|
||||
import { CoreCourseSectionSelectorComponent } from './section-selector/section-selector';
|
||||
import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsupported-module';
|
||||
|
||||
@NgModule({
|
||||
|
@ -31,6 +32,7 @@ import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsup
|
|||
CoreCourseModuleComponent,
|
||||
CoreCourseModuleCompletionComponent,
|
||||
CoreCourseModuleDescriptionComponent,
|
||||
CoreCourseSectionSelectorComponent,
|
||||
CoreCourseUnsupportedModuleComponent,
|
||||
],
|
||||
imports: [
|
||||
|
@ -45,6 +47,7 @@ import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsup
|
|||
CoreCourseModuleComponent,
|
||||
CoreCourseModuleCompletionComponent,
|
||||
CoreCourseModuleDescriptionComponent,
|
||||
CoreCourseSectionSelectorComponent,
|
||||
CoreCourseUnsupportedModuleComponent,
|
||||
],
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<core-navbar-buttons slot="end" *ngIf="loaded">
|
||||
<core-context-menu>
|
||||
<core-context-menu-item [hidden]="!displaySectionSelector || !sections || !sections.length" [priority]="500"
|
||||
[content]="'core.course.sections' | translate" (action)="showSectionSelector($event)" iconAction="menu">
|
||||
[content]="'core.course.sections' | translate" (action)="showSectionSelector()" iconAction="menu">
|
||||
</core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
|
@ -19,7 +19,7 @@
|
|||
<div *ngIf="displaySectionSelector && sections && hasSeveralSections"
|
||||
class="ion-text-wrap clearfix ion-justify-content-between core-button-selector-row"
|
||||
[class.core-section-download]="downloadEnabled">
|
||||
<ion-button class="ion-float-start" (click)="showSectionSelector($event)" color="light" aria-haspopup="true"
|
||||
<ion-button class="ion-float-start" (click)="showSectionSelector()" color="light" aria-haspopup="true"
|
||||
class="core-button-select button-no-uppercase" [attr.aria-expanded]="sectionSelectorExpanded"
|
||||
id="core-course-section-button" expand="block"> <!-- @todo: attr.aria-label? -->
|
||||
<core-icon name="fas-folder" slot="start"></core-icon>
|
||||
|
|
|
@ -46,6 +46,8 @@ import { CoreUtils } from '@services/utils/utils';
|
|||
import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks';
|
||||
import { CoreCourseSectionFormatted } from '@features/course/services/course-helper';
|
||||
import { CoreCourseModuleStatusChangedData } from '../module/module';
|
||||
import { ModalController } from '@singletons';
|
||||
import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector';
|
||||
|
||||
/**
|
||||
* Component to display course contents using a certain format. If the format isn't found, use default one.
|
||||
|
@ -330,29 +332,30 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
/**
|
||||
* Display the section selector modal.
|
||||
*
|
||||
* @param event Event.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
showSectionSelector(event?: MouseEvent): void {
|
||||
async showSectionSelector(): Promise<void> {
|
||||
if (this.sectionSelectorExpanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo this.sectionSelectorExpanded = true;
|
||||
// const modal = this.modalCtrl.create('CoreCourseSectionSelectorPage',
|
||||
// {course: this.course, sections: this.sections, selected: this.selectedSection});
|
||||
// modal.onDidDismiss((newSection) => {
|
||||
// if (newSection) {
|
||||
// this.sectionChanged(newSection);
|
||||
// }
|
||||
this.sectionSelectorExpanded = true;
|
||||
|
||||
// this.sectionSelectorExpanded = false;
|
||||
// });
|
||||
const modal = await ModalController.instance.create({
|
||||
component: CoreCourseSectionSelectorComponent,
|
||||
componentProps: {
|
||||
course: this.course,
|
||||
sections: this.sections,
|
||||
selected: this.selectedSection,
|
||||
},
|
||||
});
|
||||
await modal.present();
|
||||
|
||||
// modal.present({
|
||||
// ev: event
|
||||
// });
|
||||
const result = await modal.onWillDismiss();
|
||||
|
||||
this.sectionSelectorExpanded = false;
|
||||
if (result?.data) {
|
||||
this.sectionChanged(result?.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ 'core.course.sections' | translate }}</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
|
||||
<ion-icon slot="icon-only" name="fas-times"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list id="core-course-section-selector" role="menu">
|
||||
<ng-container *ngFor="let section of sections">
|
||||
<ion-item *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" class="ion-text-wrap"
|
||||
(click)="selectSection(section)" [class.core-primary-selected-item]="selected?.id == section.id"
|
||||
[class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail="false" role="menuitem"
|
||||
[attr.aria-hidden]="section.uservisible === false">
|
||||
|
||||
<ion-icon name="fas-folder" slot="start"></ion-icon>
|
||||
<ion-label>
|
||||
<h2><core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course?.id">
|
||||
</core-format-text></h2>
|
||||
<core-progress-bar *ngIf="section.progress >= 0" [progress]="section.progress"></core-progress-bar>
|
||||
|
||||
<ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible !== false" class="ion-text-wrap">
|
||||
{{ 'core.course.hiddenfromstudents' | translate }}
|
||||
</ion-badge>
|
||||
<ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible === false" class="ion-text-wrap">
|
||||
{{ 'core.notavailable' | translate }}
|
||||
</ion-badge>
|
||||
<ion-badge color="secondary" *ngIf="section.availabilityinfo" class="ion-text-wrap">
|
||||
<core-format-text [text]=" section.availabilityinfo" contextLevel="course" [contextInstanceId]="course?.id">
|
||||
</core-format-text>
|
||||
</ion-badge>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
</ion-content>
|
|
@ -0,0 +1,17 @@
|
|||
:host {
|
||||
core-progress-bar {
|
||||
.core-progress-text {
|
||||
line-height: 24px;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: 10px;
|
||||
}
|
||||
progress {
|
||||
margin: 8px 0 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
ion-badge {
|
||||
text-align: start;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// 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 { CoreCourseSectionFormatted } from '@features/course/services/course-helper';
|
||||
import { CoreCourseProvider } from '@features/course/services/course';
|
||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { ModalController } from '@singletons';
|
||||
|
||||
/**
|
||||
* Component to display course section selector in a modal.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-course-section-selector',
|
||||
templateUrl: 'section-selector.html',
|
||||
})
|
||||
export class CoreCourseSectionSelectorComponent implements OnInit {
|
||||
|
||||
@Input() sections?: SectionWithProgress[];
|
||||
@Input() selected?: CoreCourseSectionFormatted;
|
||||
@Input() course?: CoreCourseAnyCourseData;
|
||||
|
||||
stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
|
||||
if (!this.course || !this.sections || !this.course.enablecompletion || !('courseformatoptions' in this.course) ||
|
||||
!this.course.courseformatoptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formatOptions = CoreUtils.instance.objectToKeyValueMap(this.course.courseformatoptions, 'name', 'value');
|
||||
|
||||
if (!formatOptions || formatOptions.coursedisplay != 1 || formatOptions.completionusertracked === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sections.forEach((section) => {
|
||||
let complete = 0;
|
||||
let total = 0;
|
||||
section.modules.forEach((module) => {
|
||||
if (!module.uservisible || module.completiondata === undefined || module.completiondata.tracking === undefined ||
|
||||
module.completiondata.tracking <= CoreCourseProvider.COMPLETION_TRACKING_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
total++;
|
||||
if (module.completiondata.state == CoreCourseProvider.COMPLETION_COMPLETE ||
|
||||
module.completiondata.state == CoreCourseProvider.COMPLETION_COMPLETE_PASS) {
|
||||
complete++;
|
||||
}
|
||||
});
|
||||
|
||||
if (total > 0) {
|
||||
section.progress = complete / total * 100;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the modal.
|
||||
*/
|
||||
closeModal(): void {
|
||||
ModalController.instance.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a section.
|
||||
*
|
||||
* @param section Selected section object.
|
||||
*/
|
||||
selectSection(section: SectionWithProgress): void {
|
||||
if (section.uservisible !== false) {
|
||||
ModalController.instance.dismiss(section);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type SectionWithProgress = CoreCourseSectionFormatted & {
|
||||
progress?: number;
|
||||
};
|
|
@ -169,6 +169,9 @@ ion-toolbar {
|
|||
.item.core-danger-item {
|
||||
--border-color: var(--ion-color-danger);
|
||||
}
|
||||
.item-dimmed {
|
||||
opacity: 0.71;
|
||||
}
|
||||
|
||||
// Extra text colors.
|
||||
.text-gray {
|
||||
|
|
Loading…
Reference in New Issue