MOBILE-4442 course: Create a separate component to manage sections
parent
647fdf8bf3
commit
0b4c3dc88e
|
@ -8,10 +8,12 @@
|
||||||
<core-loading [hideUntil]="loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
|
|
||||||
<!-- Single section. -->
|
<!-- Single section. -->
|
||||||
<div *ngIf="selectedSection && selectedSection.id !== allSectionsId" class="single-section list-item-limited-width">
|
<div *ngIf="selectedSection && selectedSection.id !== allSectionsId" class="list-item-limited-width">
|
||||||
<core-dynamic-component [component]="singleSectionComponent" [data]="data">
|
<core-dynamic-component [component]="singleSectionComponent" [data]="data">
|
||||||
<ion-accordion-group [readonly]="true" value="single">
|
<ion-accordion-group [readonly]="true" value="non-collapsible">
|
||||||
<ng-container *ngTemplateOutlet="sectionTemplate; context: {section: selectedSection, sectionId: 'single'}" />
|
<core-course-section *ngIf="!selectedSection.hiddenbynumsections && selectedSection.id !== allSectionsId &&
|
||||||
|
selectedSection.id !== stealthModulesSectionId" [course]="course" [section]="selectedSection"
|
||||||
|
[lastModuleViewed]="lastModuleViewed" [viewedModules]="viewedModules" [collapsible]="false" />
|
||||||
</ion-accordion-group>
|
</ion-accordion-group>
|
||||||
<core-empty-box *ngIf="!selectedSection.hasContent" icon="fas-table-cells-large"
|
<core-empty-box *ngIf="!selectedSection.hasContent" icon="fas-table-cells-large"
|
||||||
[message]="'core.course.nocontentavailable' | translate" />
|
[message]="'core.course.nocontentavailable' | translate" />
|
||||||
|
@ -19,13 +21,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Multiple sections. -->
|
<!-- Multiple sections. -->
|
||||||
<div *ngIf="selectedSection && selectedSection.id === allSectionsId" class="multiple-sections list-item-limited-width">
|
<div *ngIf="selectedSection && selectedSection.id === allSectionsId" class="list-item-limited-width">
|
||||||
<core-dynamic-component [component]="allSectionsComponent" [data]="data">
|
<core-dynamic-component [component]="allSectionsComponent" [data]="data">
|
||||||
<ion-accordion-group [multiple]="true" (ionChange)="accordionMultipleChange($event.detail)" [value]="accordionMultipleValue"
|
<ion-accordion-group [multiple]="true" (ionChange)="accordionMultipleChange($event.detail)" [value]="accordionMultipleValue"
|
||||||
#accordionMultiple>
|
#accordionMultiple>
|
||||||
@for (section of sections; track section.id) {
|
@for (section of sections; track section.id) {
|
||||||
@if ($index <= lastShownSectionIndex) {
|
@if ($index <= lastShownSectionIndex) {
|
||||||
<ng-container *ngTemplateOutlet="sectionTemplate; context: {section: section, sectionId: section.id}" />
|
<core-course-section
|
||||||
|
*ngIf="!section.hiddenbynumsections && section.id !== allSectionsId && section.id !== stealthModulesSectionId"
|
||||||
|
[course]="course" [section]="section" [lastModuleViewed]="lastModuleViewed" [viewedModules]="viewedModules"
|
||||||
|
[collapsible]="true" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</ion-accordion-group>
|
</ion-accordion-group>
|
||||||
|
@ -65,55 +70,3 @@
|
||||||
<ion-icon name="fas-list-ul" aria-hidden="true" />
|
<ion-icon name="fas-list-ul" aria-hidden="true" />
|
||||||
</ion-fab-button>
|
</ion-fab-button>
|
||||||
</ion-fab>
|
</ion-fab>
|
||||||
|
|
||||||
<!-- Template to render a section. -->
|
|
||||||
<ng-template #sectionTemplate let-section="section" let-sectionId="sectionId">
|
|
||||||
<ion-accordion *ngIf="!section.hiddenbynumsections && section.id !== allSectionsId && section.id !== stealthModulesSectionId"
|
|
||||||
class="core-course-module-list-wrapper" [id]="section.id"
|
|
||||||
[attr.aria-labelledby]="section.name ? 'core-section-name-' + section.id : null" [value]="''+sectionId" toggleIconSlot="start">
|
|
||||||
<ion-item class="course-section divider" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" slot="header">
|
|
||||||
<ion-label class="ion-text-wrap">
|
|
||||||
<h2 *ngIf="section.name" class="big" [id]="'core-section-name-' + section.id">
|
|
||||||
<core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course.id" />
|
|
||||||
</h2>
|
|
||||||
<div *ngIf="section.visible === 0 && section.uservisible !== false">
|
|
||||||
<ion-badge color="warning">
|
|
||||||
{{ 'core.course.hiddenfromstudents' | translate }}
|
|
||||||
</ion-badge>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="section.visible === 0 && section.uservisible === false">
|
|
||||||
<ion-badge color="warning">
|
|
||||||
{{ 'core.notavailable' | translate }}
|
|
||||||
</ion-badge>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="section.availabilityinfo">
|
|
||||||
<ion-chip class="clickable">
|
|
||||||
<ion-icon name="fas-lock" [attr.aria-label]="'core.restricted' | translate" />
|
|
||||||
<ion-label>
|
|
||||||
<core-format-text [text]=" section.availabilityinfo" contextLevel="course" [contextInstanceId]="course.id" />
|
|
||||||
</ion-label>
|
|
||||||
</ion-chip>
|
|
||||||
</div>
|
|
||||||
</ion-label>
|
|
||||||
<ion-badge *ngIf="section.highlighted && highlighted" slot="end">{{highlighted}}</ion-badge>
|
|
||||||
</ion-item>
|
|
||||||
|
|
||||||
<div slot="content">
|
|
||||||
<ng-container *ngIf="section.expanded">
|
|
||||||
<ion-item class="ion-text-wrap section-summary" *ngIf="section.summary">
|
|
||||||
<ion-label>
|
|
||||||
<core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course.id" />
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
|
|
||||||
<ng-container *ngFor="let module of section.modules">
|
|
||||||
<core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [section]="section"
|
|
||||||
[showActivityDates]="course.showactivitydates" [showCompletionConditions]="course.showcompletionconditions"
|
|
||||||
[isLastViewed]="lastModuleViewed && lastModuleViewed.cmId === module.id"
|
|
||||||
[class.core-course-module-not-viewed]="
|
|
||||||
!viewedModules[module.id] && (!module.completiondata || module.completiondata.state === completionStatusIncomplete)" />
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</ion-accordion>
|
|
||||||
</ng-template>
|
|
||||||
|
|
|
@ -10,30 +10,3 @@
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.course-section {
|
|
||||||
--inner-padding-end: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multiple-sections .core-course-module-list-wrapper {
|
|
||||||
border: var(--ion-card-border-width) solid var(--ion-card-border-color);
|
|
||||||
border-radius: var(--ion-card-radius);
|
|
||||||
margin: 8px 4px;
|
|
||||||
width: calc(100% - 8px);
|
|
||||||
|
|
||||||
ion-card {
|
|
||||||
--ion-card-background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-item.divider.course-section {
|
|
||||||
--background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.single-section ::ng-deep {
|
|
||||||
ion-item.divider.course-section {
|
|
||||||
ion-icon.ion-accordion-toggle-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
Input,
|
Input,
|
||||||
|
@ -31,7 +30,6 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp
|
||||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||||
import {
|
import {
|
||||||
CoreCourse,
|
CoreCourse,
|
||||||
CoreCourseModuleCompletionStatus,
|
|
||||||
CoreCourseProvider,
|
CoreCourseProvider,
|
||||||
} from '@features/course/services/course';
|
} from '@features/course/services/course';
|
||||||
import {
|
import {
|
||||||
|
@ -56,12 +54,12 @@ import { ContextLevel } from '@/core/constants';
|
||||||
import { CoreModals } from '@services/modals';
|
import { CoreModals } from '@services/modals';
|
||||||
import { CoreSharedModule } from '@/core/shared.module';
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
import { CoreBlockComponentsModule } from '@features/block/components/components.module';
|
import { CoreBlockComponentsModule } from '@features/block/components/components.module';
|
||||||
import { CoreCourseComponentsModule } from '../components.module';
|
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { COURSE_ALL_SECTIONS_PREFERRED_PREFIX, COURSE_EXPANDED_SECTIONS_PREFIX } from '@features/course/constants';
|
import { COURSE_ALL_SECTIONS_PREFERRED_PREFIX, COURSE_EXPANDED_SECTIONS_PREFIX } from '@features/course/constants';
|
||||||
import { toBoolean } from '@/core/transforms/boolean';
|
import { toBoolean } from '@/core/transforms/boolean';
|
||||||
import { CoreInfiniteLoadingComponent } from '@components/infinite-loading/infinite-loading';
|
import { CoreInfiniteLoadingComponent } from '@components/infinite-loading/infinite-loading';
|
||||||
import { CoreSite } from '@classes/sites/site';
|
import { CoreSite } from '@classes/sites/site';
|
||||||
|
import { CoreCourseSectionComponent, CoreCourseSectionToDisplay } from '../course-section/course-section';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to display course contents using a certain format. If the format isn't found, use default one.
|
* Component to display course contents using a certain format. If the format isn't found, use default one.
|
||||||
|
@ -76,12 +74,12 @@ import { CoreSite } from '@classes/sites/site';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'core-course-format',
|
selector: 'core-course-format',
|
||||||
templateUrl: 'course-format.html',
|
templateUrl: 'course-format.html',
|
||||||
styleUrls: ['course-format.scss'],
|
styleUrl: 'course-format.scss',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
CoreSharedModule,
|
CoreSharedModule,
|
||||||
|
CoreCourseSectionComponent,
|
||||||
CoreBlockComponentsModule,
|
CoreBlockComponentsModule,
|
||||||
CoreCourseComponentsModule,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
|
@ -129,13 +127,11 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
selectedSection?: CoreCourseSectionToDisplay;
|
selectedSection?: CoreCourseSectionToDisplay;
|
||||||
previousSection?: CoreCourseSectionToDisplay;
|
previousSection?: CoreCourseSectionToDisplay;
|
||||||
nextSection?: CoreCourseSectionToDisplay;
|
nextSection?: CoreCourseSectionToDisplay;
|
||||||
allSectionsId: number = CoreCourseProvider.ALL_SECTIONS_ID;
|
allSectionsId = CoreCourseProvider.ALL_SECTIONS_ID;
|
||||||
stealthModulesSectionId: number = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
|
stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
|
||||||
loaded = false;
|
loaded = false;
|
||||||
highlighted?: string;
|
|
||||||
lastModuleViewed?: CoreCourseViewedModulesDBRecord;
|
lastModuleViewed?: CoreCourseViewedModulesDBRecord;
|
||||||
viewedModules: Record<number, boolean> = {};
|
viewedModules: Record<number, boolean> = {};
|
||||||
completionStatusIncomplete = CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE;
|
|
||||||
|
|
||||||
communicationRoomUrl?: string;
|
communicationRoomUrl?: string;
|
||||||
|
|
||||||
|
@ -256,7 +252,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
// Format has changed or it's the first time, load all the components.
|
// Format has changed or it's the first time, load all the components.
|
||||||
this.lastCourseFormat = this.course.format;
|
this.lastCourseFormat = this.course.format;
|
||||||
|
|
||||||
this.highlighted = CoreCourseFormatDelegate.getSectionHightlightedName(this.course);
|
|
||||||
const currentSectionData = await CoreCourseFormatDelegate.getCurrentSection(this.course, this.sections);
|
const currentSectionData = await CoreCourseFormatDelegate.getCurrentSection(this.course, this.sections);
|
||||||
currentSectionData.section.highlighted = true;
|
currentSectionData.section.highlighted = true;
|
||||||
|
|
||||||
|
@ -301,8 +296,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
* @param sections Sections to treat.
|
* @param sections Sections to treat.
|
||||||
*/
|
*/
|
||||||
protected async treatSections(sections: CoreCourseSectionToDisplay[]): Promise<void> {
|
protected async treatSections(sections: CoreCourseSectionToDisplay[]): Promise<void> {
|
||||||
const hasAllSections = sections[0].id == CoreCourseProvider.ALL_SECTIONS_ID;
|
const hasAllSections = sections[0].id === CoreCourseProvider.ALL_SECTIONS_ID;
|
||||||
const hasSeveralSections = sections.length > 2 || (sections.length == 2 && !hasAllSections);
|
const hasSeveralSections = sections.length > 2 || (sections.length === 2 && !hasAllSections);
|
||||||
|
|
||||||
await this.initializeViewedModules();
|
await this.initializeViewedModules();
|
||||||
if (this.selectedSection) {
|
if (this.selectedSection) {
|
||||||
|
@ -820,8 +815,3 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CoreCourseSectionToDisplay = CoreCourseSection & {
|
|
||||||
highlighted?: boolean;
|
|
||||||
expanded?: boolean; // The aim of this property is to avoid DOM overloading.
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<ion-accordion *ngIf="!section.hiddenbynumsections" class="core-course-module-list-wrapper" [id]="section.id"
|
||||||
|
[attr.aria-labelledby]="section.name ? 'core-section-name-' + section.id : null" [value]="collapsible ? section.id : 'non-collapsible'"
|
||||||
|
toggleIconSlot="start">
|
||||||
|
<ion-item class="course-section divider" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" slot="header">
|
||||||
|
<ion-label class="ion-text-wrap">
|
||||||
|
<h2 *ngIf="section.name" class="big" [id]="'core-section-name-' + section.id">
|
||||||
|
<core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course.id" />
|
||||||
|
</h2>
|
||||||
|
<div *ngIf="section.visible === 0 && section.uservisible !== false">
|
||||||
|
<ion-badge color="warning">
|
||||||
|
{{ 'core.course.hiddenfromstudents' | translate }}
|
||||||
|
</ion-badge>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="section.visible === 0 && section.uservisible === false">
|
||||||
|
<ion-badge color="warning">
|
||||||
|
{{ 'core.notavailable' | translate }}
|
||||||
|
</ion-badge>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="section.availabilityinfo">
|
||||||
|
<ion-chip class="clickable">
|
||||||
|
<ion-icon name="fas-lock" [attr.aria-label]="'core.restricted' | translate" />
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]=" section.availabilityinfo" contextLevel="course" [contextInstanceId]="course.id" />
|
||||||
|
</ion-label>
|
||||||
|
</ion-chip>
|
||||||
|
</div>
|
||||||
|
</ion-label>
|
||||||
|
<ion-badge *ngIf="section.highlighted && highlightedName" slot="end">{{highlightedName}}</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<div slot="content">
|
||||||
|
<ng-container *ngIf="section.expanded">
|
||||||
|
<ion-item class="ion-text-wrap section-summary" *ngIf="section.summary">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course.id" />
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ng-container *ngFor="let module of section.modules">
|
||||||
|
<core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [section]="section"
|
||||||
|
[showActivityDates]="course.showactivitydates" [showCompletionConditions]="course.showcompletionconditions"
|
||||||
|
[isLastViewed]="lastModuleViewed && lastModuleViewed.cmId === module.id" [class.core-course-module-not-viewed]="
|
||||||
|
!viewedModules[module.id] &&
|
||||||
|
(!module.completiondata || module.completiondata.state === completionStatusIncomplete)" />
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ion-accordion>
|
|
@ -0,0 +1,30 @@
|
||||||
|
:host {
|
||||||
|
.course-section {
|
||||||
|
--inner-padding-end: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.collapsible {
|
||||||
|
.core-course-module-list-wrapper {
|
||||||
|
border: var(--ion-card-border-width) solid var(--ion-card-border-color);
|
||||||
|
border-radius: var(--ion-card-radius);
|
||||||
|
margin: 8px 4px;
|
||||||
|
width: calc(100% - 8px);
|
||||||
|
|
||||||
|
ion-card {
|
||||||
|
--ion-card-background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-item.divider.course-section {
|
||||||
|
--background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.non-collapsible ::ng-deep {
|
||||||
|
ion-item.divider.course-section {
|
||||||
|
ion-icon.ion-accordion-toggle-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
// (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,
|
||||||
|
HostBinding,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
CoreCourseSection,
|
||||||
|
} from '@features/course/services/course-helper';
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { CoreCourseComponentsModule } from '../components.module';
|
||||||
|
import { toBoolean } from '@/core/transforms/boolean';
|
||||||
|
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||||
|
import { CoreCourseViewedModulesDBRecord } from '@features/course/services/database/course';
|
||||||
|
import { CoreCourseModuleCompletionStatus } from '@features/course/services/course';
|
||||||
|
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to display course section.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-course-section',
|
||||||
|
templateUrl: 'course-section.html',
|
||||||
|
styleUrl: 'course-section.scss',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CoreSharedModule,
|
||||||
|
CoreCourseComponentsModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CoreCourseSectionComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input({ required: true }) course!: CoreCourseAnyCourseData; // The course to render.
|
||||||
|
@Input({ required: true }) section!: CoreCourseSectionToDisplay;
|
||||||
|
@Input({ transform: toBoolean }) collapsible = true; // Whether the section can be collapsed.
|
||||||
|
@Input() lastModuleViewed?: CoreCourseViewedModulesDBRecord;
|
||||||
|
@Input() viewedModules: Record<number, boolean> = {};
|
||||||
|
|
||||||
|
@HostBinding('class')
|
||||||
|
get collapsibleClass(): string {
|
||||||
|
return this.collapsible ? 'collapsible' : 'non-collapsible';
|
||||||
|
}
|
||||||
|
|
||||||
|
completionStatusIncomplete = CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE;
|
||||||
|
highlightedName?: string; // Name to highlight.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.highlightedName = this.section.highlighted && this.highlightedName === undefined
|
||||||
|
? CoreCourseFormatDelegate.getSectionHightlightedName(this.course)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CoreCourseSectionToDisplay = CoreCourseSection & {
|
||||||
|
highlighted?: boolean;
|
||||||
|
expanded?: boolean; // The aim of this property is to avoid DOM overloading.
|
||||||
|
};
|
Loading…
Reference in New Issue