MOBILE-2310 core: Implement and apply core-tabs and core-tab
parent
e1bc11e44b
commit
cbc983637d
|
@ -12,18 +12,6 @@
|
||||||
height: calc(100% - #{($card-ios-margin-end + $card-ios-margin-start)});
|
height: calc(100% - #{($card-ios-margin-end + $card-ios-margin-start)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top tabs
|
|
||||||
// -------------------------
|
|
||||||
.ios .core-top-tabbar {
|
|
||||||
-webkit-box-pack: center;
|
|
||||||
-webkit-justify-content: center;
|
|
||||||
-ms-flex-pack: center;
|
|
||||||
justify-content: center;
|
|
||||||
> a {
|
|
||||||
font-size: 1.6rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-buttons core-context-menu .button-clear-ios {
|
.bar-buttons core-context-menu .button-clear-ios {
|
||||||
color: $toolbar-ios-button-color;
|
color: $toolbar-ios-button-color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,32 +255,6 @@ ion-select {
|
||||||
position: relative
|
position: relative
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top tabs
|
|
||||||
// -------------------------
|
|
||||||
|
|
||||||
.core-top-tabbar {
|
|
||||||
@include position(null, null, 0, 0);
|
|
||||||
|
|
||||||
z-index: $z-index-toolbar;
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
background: $core-top-tabs-background;
|
|
||||||
|
|
||||||
> a {
|
|
||||||
@extend .tab-button;
|
|
||||||
|
|
||||||
background: $core-top-tabs-background;
|
|
||||||
color: $core-top-tabs-color !important;
|
|
||||||
border-bottom: 1px solid $core-top-tabs-border;
|
|
||||||
font-size: 1.6rem;
|
|
||||||
|
|
||||||
&[aria-selected=true] {
|
|
||||||
color: $core-top-tabs-color-active !important;
|
|
||||||
border-bottom: 2px solid $core-top-tabs-color-active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// File uploader.
|
// File uploader.
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ import { CoreCoursePickerMenuPopoverComponent } from './course-picker-menu/cours
|
||||||
import { CoreChronoComponent } from './chrono/chrono';
|
import { CoreChronoComponent } from './chrono/chrono';
|
||||||
import { CoreLocalFileComponent } from './local-file/local-file';
|
import { CoreLocalFileComponent } from './local-file/local-file';
|
||||||
import { CoreSitePickerComponent } from './site-picker/site-picker';
|
import { CoreSitePickerComponent } from './site-picker/site-picker';
|
||||||
|
import { CoreTabsComponent } from './tabs/tabs';
|
||||||
|
import { CoreTabComponent } from './tabs/tab';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -53,7 +55,9 @@ import { CoreSitePickerComponent } from './site-picker/site-picker';
|
||||||
CoreCoursePickerMenuPopoverComponent,
|
CoreCoursePickerMenuPopoverComponent,
|
||||||
CoreChronoComponent,
|
CoreChronoComponent,
|
||||||
CoreLocalFileComponent,
|
CoreLocalFileComponent,
|
||||||
CoreSitePickerComponent
|
CoreSitePickerComponent,
|
||||||
|
CoreTabsComponent,
|
||||||
|
CoreTabComponent
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
CoreContextMenuPopoverComponent,
|
CoreContextMenuPopoverComponent,
|
||||||
|
@ -80,7 +84,9 @@ import { CoreSitePickerComponent } from './site-picker/site-picker';
|
||||||
CoreContextMenuItemComponent,
|
CoreContextMenuItemComponent,
|
||||||
CoreChronoComponent,
|
CoreChronoComponent,
|
||||||
CoreLocalFileComponent,
|
CoreLocalFileComponent,
|
||||||
CoreSitePickerComponent
|
CoreSitePickerComponent,
|
||||||
|
CoreTabsComponent,
|
||||||
|
CoreTabComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreComponentsModule {}
|
export class CoreComponentsModule {}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
// (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, Output, OnInit, OnDestroy, ElementRef, EventEmitter } from '@angular/core';
|
||||||
|
import { CoreTabsComponent } from './tabs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tab to use inside core-tabs. The content of this tab will be displayed when the tab is selected.
|
||||||
|
*
|
||||||
|
* You must provide either a title or an icon for the tab.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
*
|
||||||
|
* <core-tabs selectedIndex="1">
|
||||||
|
* <core-tab [title]="'core.courses.timeline' | translate" (ionSelect)="switchTab('timeline')">
|
||||||
|
* <!-- Tab contents. -->
|
||||||
|
* </core-tab>
|
||||||
|
* </core-tabs>
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-tab',
|
||||||
|
template: '<ng-content></ng-content>'
|
||||||
|
})
|
||||||
|
export class CoreTabComponent implements OnInit, OnDestroy {
|
||||||
|
@Input() title?: string; // The tab title.
|
||||||
|
@Input() icon?: string; // The tab icon.
|
||||||
|
@Input() badge?: string; // A badge to add in the tab.
|
||||||
|
@Input() badgeStyle?: string; // The badge color.
|
||||||
|
@Input() enabled?: boolean = true; // Whether the tab is enabled.
|
||||||
|
@Input() show?: boolean = true; // Whether the tab should be shown.
|
||||||
|
@Input() id?: string; // An ID to identify the tab.
|
||||||
|
@Output() ionSelect: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>();
|
||||||
|
|
||||||
|
element: HTMLElement; // The core-tab element.
|
||||||
|
|
||||||
|
constructor(private tabs: CoreTabsComponent, element: ElementRef) {
|
||||||
|
this.element = element.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit() {
|
||||||
|
this.tabs.addTab(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.tabs.removeTab(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="core-tabs-bar">
|
||||||
|
<ng-container *ngFor="let tab of tabs; let idx = index">
|
||||||
|
<a *ngIf="tab.show" [attr.aria-selected]="selected == idx" (click)="selectTab(idx)">
|
||||||
|
<ion-icon *ngIf="tab.icon" [name]="tab.icon"></ion-icon>
|
||||||
|
<span *ngIf="tab.title">{{ tab.title }}</span>
|
||||||
|
<ion-badge *ngIf="tab.badge" [color]="tab.badgeStyle" class="tab-badge">{{tab.badge}}</ion-badge>
|
||||||
|
</a>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div #originalTabs>
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
|
@ -0,0 +1,11 @@
|
||||||
|
core-tabs {
|
||||||
|
.core-tabs-bar {
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
-webkit-justify-content: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
justify-content: center;
|
||||||
|
> a {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
core-tabs {
|
||||||
|
.core-tabs-bar {
|
||||||
|
@include position(null, null, 0, 0);
|
||||||
|
|
||||||
|
z-index: $z-index-toolbar;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
background: $core-top-tabs-background;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
@extend .tab-button;
|
||||||
|
|
||||||
|
background: $core-top-tabs-background;
|
||||||
|
color: $core-top-tabs-color !important;
|
||||||
|
border-bottom: 1px solid $core-top-tabs-border;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
|
||||||
|
&[aria-selected=true] {
|
||||||
|
color: $core-top-tabs-color-active !important;
|
||||||
|
border-bottom: 2px solid $core-top-tabs-color-active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core-tab {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
// (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, Output, EventEmitter, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
|
||||||
|
import { CoreTabComponent } from './tab';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component displays some tabs that usually share data between them.
|
||||||
|
*
|
||||||
|
* If your tabs don't share any data then you should probably use ion-tabs. This component doesn't use different ion-nav
|
||||||
|
* for each tab, so it will not load pages.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
*
|
||||||
|
* <core-tabs selectedIndex="1">
|
||||||
|
* <core-tab [title]="'core.courses.timeline' | translate" (ionSelect)="switchTab('timeline')">
|
||||||
|
* <!-- Tab contents. -->
|
||||||
|
* </core-tab>
|
||||||
|
* </core-tabs>
|
||||||
|
*
|
||||||
|
* Obviously, the tab contents will only be shown if that tab is selected.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-tabs',
|
||||||
|
templateUrl: 'tabs.html'
|
||||||
|
})
|
||||||
|
export class CoreTabsComponent implements OnInit, AfterViewInit {
|
||||||
|
@Input() selectedIndex?: number = 0; // Index of the tab to select.
|
||||||
|
@Output() ionChange: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>(); // Emitted when the tab changes.
|
||||||
|
@ViewChild('originalTabs') originalTabsRef: ElementRef;
|
||||||
|
|
||||||
|
tabs: CoreTabComponent[] = []; // List of tabs.
|
||||||
|
selected: number; // Selected tab number.
|
||||||
|
protected originalTabsContainer: HTMLElement; // The container of the original tabs. It will include each tab's content.
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit() {
|
||||||
|
this.originalTabsContainer = this.originalTabsRef.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View has been initialized.
|
||||||
|
*/
|
||||||
|
ngAfterViewInit() {
|
||||||
|
let selectedIndex = this.selectedIndex || 0,
|
||||||
|
selectedTab = this.tabs[selectedIndex];
|
||||||
|
|
||||||
|
if (!selectedTab.enabled || !selectedTab.show) {
|
||||||
|
// The tab is not enabled or not shown. Get the first tab that is enabled.
|
||||||
|
selectedTab = this.tabs.find((tab, index) => {
|
||||||
|
if (tab.enabled && tab.show) {
|
||||||
|
selectedIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedTab) {
|
||||||
|
this.selectTab(selectedIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new tab if it isn't already in the list of tabs.
|
||||||
|
*
|
||||||
|
* @param {CoreTabComponent} tab The tab to add.
|
||||||
|
*/
|
||||||
|
addTab(tab: CoreTabComponent) : void {
|
||||||
|
// Check if tab is already in the list.
|
||||||
|
if (this.getIndex(tab) == -1) {
|
||||||
|
this.tabs.push(tab);
|
||||||
|
this.sortTabs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index of tab.
|
||||||
|
*
|
||||||
|
* @param {any} tab [description]
|
||||||
|
* @return {number} [description]
|
||||||
|
*/
|
||||||
|
getIndex(tab: any) : number {
|
||||||
|
for (let i = 0; i < this.tabs.length; i++) {
|
||||||
|
let t = this.tabs[i];
|
||||||
|
if (t === tab || (typeof t.id != 'undefined' && t.id === tab.id)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current selected tab.
|
||||||
|
*
|
||||||
|
* @return {CoreTabComponent} Selected tab.
|
||||||
|
*/
|
||||||
|
getSelected() : CoreTabComponent {
|
||||||
|
return this.tabs[this.selected];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a tab from the list of tabs.
|
||||||
|
*
|
||||||
|
* @param {CoreTabComponent} tab The tab to remove.
|
||||||
|
*/
|
||||||
|
removeTab(tab: CoreTabComponent) : void {
|
||||||
|
const index = this.getIndex(tab);
|
||||||
|
this.tabs.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a certain tab.
|
||||||
|
*
|
||||||
|
* @param {number} index The index of the tab to select.
|
||||||
|
*/
|
||||||
|
selectTab(index: number) : void {
|
||||||
|
if (index == this.selected) {
|
||||||
|
// Already selected.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 0 || index >= this.tabs.length) {
|
||||||
|
// Index isn't valid, select the first one.
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currenTab = this.getSelected(),
|
||||||
|
newTab = this.tabs[index];
|
||||||
|
|
||||||
|
if (!newTab.enabled || !newTab.show) {
|
||||||
|
// The tab isn't enabled or shown, stop.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currenTab) {
|
||||||
|
// Unselect previous selected tab.
|
||||||
|
currenTab.element.classList.remove('selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selected = index;
|
||||||
|
newTab.element.classList.add('selected');
|
||||||
|
newTab.ionSelect.emit(newTab);
|
||||||
|
this.ionChange.emit(newTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the tabs, keeping the same order as in the original list.
|
||||||
|
*/
|
||||||
|
protected sortTabs() {
|
||||||
|
if (this.originalTabsContainer) {
|
||||||
|
let newTabs = [],
|
||||||
|
newSelected;
|
||||||
|
|
||||||
|
this.tabs.forEach((tab, index) => {
|
||||||
|
let originalIndex = Array.prototype.indexOf.call(this.originalTabsContainer.children, tab.element);
|
||||||
|
if (originalIndex != -1) {
|
||||||
|
newTabs[originalIndex] = tab;
|
||||||
|
if (this.selected == index) {
|
||||||
|
newSelected = originalIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tabs = newTabs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,64 +17,66 @@
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
|
|
||||||
<!-- @todo: Use a component to render the tabs. -->
|
<core-tabs selectedIndex="1">
|
||||||
<div class="core-top-tabbar">
|
<!-- Timeline tab. -->
|
||||||
<a [attr.aria-selected]="tabShown == 'timeline'" (click)="switchTab('timeline')">{{ 'core.courses.timeline' | translate }}</a>
|
<core-tab [title]="'core.courses.timeline' | translate" (ionSelect)="tabChanged('timeline')">
|
||||||
<a [attr.aria-selected]="tabShown == 'courses'" (click)="switchTab('courses')">{{ 'core.courses.courses' | translate }}</a>
|
<div no-padding [hidden]="!(timeline.loaded || timelineCourses.loaded)">
|
||||||
</div>
|
<ion-select [(ngModel)]="timeline.sort" (ngModelChange)="switchSort()">
|
||||||
|
<ion-option value="sortbydates">{{ 'core.courses.sortbydates' | translate }}</ion-option>
|
||||||
|
<ion-option value="sortbycourses">{{ 'core.courses.sortbycourses' | translate }}</ion-option>
|
||||||
|
</ion-select>
|
||||||
|
</div>
|
||||||
|
<core-loading [hideUntil]="timeline.loaded" [hidden]="timeline.sort != 'sortbydates'" class="core-loading-center">
|
||||||
|
<core-courses-overview-events [events]="timeline.events" showCourse="true" [canLoadMore]="timeline.canLoadMore" (loadMore)="loadMoreTimeline()"></core-courses-overview-events>
|
||||||
|
</core-loading>
|
||||||
|
<core-loading [hideUntil]="timelineCourses.loaded" [hidden]="timeline.sort != 'sortbycourses'" class="core-loading-center">
|
||||||
|
<ion-grid no-padding>
|
||||||
|
<ion-row no-padding>
|
||||||
|
<ion-col *ngFor="let course of timelineCourses.courses" no-padding col-12 col-md-6>
|
||||||
|
<core-courses-course-progress [course]="course">
|
||||||
|
<core-courses-overview-events [events]="course.events" [canLoadMore]="course.canLoadMore" (loadMore)="loadMoreCourse(course)"></core-courses-overview-events>
|
||||||
|
</core-courses-course-progress>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
|
<core-empty-box *ngIf="timelineCourses.courses.length == 0" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursesoverview' | translate"></core-empty-box>
|
||||||
|
</core-loading>
|
||||||
|
</core-tab>
|
||||||
|
|
||||||
<div [hidden]="tabShown != 'timeline'">
|
<!-- Courses tab. -->
|
||||||
<div no-padding [hidden]="!(timeline.loaded || timelineCourses.loaded)">
|
<core-tab [title]="'core.courses.courses' | translate" (ionSelect)="tabChanged('courses')">
|
||||||
<ion-select [(ngModel)]="timeline.sort" (ngModelChange)="switchSort()">
|
<core-loading [hideUntil]="courses.loaded" class="core-loading-center">
|
||||||
<ion-option value="sortbydates">{{ 'core.courses.sortbydates' | translate }}</ion-option>
|
<div no-padding class="clearfix" [hidden]="showFilter">
|
||||||
<ion-option value="sortbycourses">{{ 'core.courses.sortbycourses' | translate }}</ion-option>
|
<ion-select [title]="'core.show' | translate" [(ngModel)]="courses.selected" float-start (ngModelChange)="selectedChanged()">
|
||||||
</ion-select>
|
<ion-option value="inprogress">{{ 'core.courses.inprogress' | translate }}</ion-option>
|
||||||
</div>
|
<ion-option value="future">{{ 'core.courses.future' | translate }}</ion-option>
|
||||||
<core-loading [hideUntil]="timeline.loaded" [hidden]="tabShown != 'timeline' || timeline.sort != 'sortbydates'" class="core-loading-center">
|
<ion-option value="past">{{ 'core.courses.past' | translate }}</ion-option>
|
||||||
<core-courses-overview-events [events]="timeline.events" showCourse="true" [canLoadMore]="timeline.canLoadMore" (loadMore)="loadMoreTimeline()"></core-courses-overview-events>
|
</ion-select>
|
||||||
</core-loading>
|
<button [hidden]="!courses[courses.selected] || !courses[courses.selected].length" ion-button icon-only clear color="dark" float-end>
|
||||||
<core-loading [hideUntil]="timelineCourses.loaded" [hidden]="tabShown != 'timeline' || timeline.sort != 'sortbycourses'" class="core-loading-center">
|
<ion-icon name="cloud-download"></ion-icon>
|
||||||
<ion-grid no-padding>
|
</button>
|
||||||
<ion-row no-padding>
|
</div>
|
||||||
<ion-col *ngFor="let course of timelineCourses.courses" no-padding col-12 col-md-6>
|
<div no-padding padding-bottom [hidden]="!showFilter">
|
||||||
<core-courses-course-progress [course]="course">
|
<ion-item>
|
||||||
<core-courses-overview-events [events]="course.events" [canLoadMore]="course.canLoadMore" (loadMore)="loadMoreCourse(course)"></core-courses-overview-events>
|
<ion-label><ion-icon name="funnel" class="placeholder-icon"></ion-icon></ion-label>
|
||||||
</core-courses-course-progress>
|
<ion-input type="text" name="filter" clearInput [(ngModel)]="courses.filter" (ngModelChange)="filterChanged($event)" [placeholder]="'core.courses.filtermycourses' | translate"></ion-input>
|
||||||
</ion-col>
|
</ion-item>
|
||||||
</ion-row>
|
</div>
|
||||||
</ion-grid>
|
<div>
|
||||||
<core-empty-box *ngIf="timelineCourses.courses.length == 0" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursesoverview' | translate"></core-empty-box>
|
<ion-grid no-padding>
|
||||||
</core-loading>
|
<ion-row no-padding>
|
||||||
</div>
|
<ion-col *ngFor="let course of filteredCourses" no-padding col-12 col-sm-6 col-md-6 col-lg-4 col-xl-4 align-self-stretch>
|
||||||
<core-loading [hideUntil]="courses.loaded" [hidden]="tabShown != 'courses'" class="core-loading-center">
|
<core-courses-course-progress [course]="course" class="core-courseoverview"></core-courses-course-progress>
|
||||||
<div no-padding class="clearfix" [hidden]="showFilter">
|
</ion-col>
|
||||||
<ion-select [title]="'core.show' | translate" [(ngModel)]="courses.selected" float-start (ngModelChange)="selectedChanged()">
|
</ion-row>
|
||||||
<ion-option value="inprogress">{{ 'core.courses.inprogress' | translate }}</ion-option>
|
</ion-grid>
|
||||||
<ion-option value="future">{{ 'core.courses.future' | translate }}</ion-option>
|
|
||||||
<ion-option value="past">{{ 'core.courses.past' | translate }}</ion-option>
|
<core-empty-box *ngIf="courses[courses.selected].length == 0 && courses.selected == 'inprogress'" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursesinprogress' | translate"></core-empty-box>
|
||||||
</ion-select>
|
<core-empty-box *ngIf="courses[courses.selected].length == 0 && courses.selected == 'future'" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursesfuture' | translate"></core-empty-box>
|
||||||
<button [hidden]="!courses[courses.selected] || !courses[courses.selected].length" ion-button icon-only clear color="dark" float-end>
|
<core-empty-box *ngIf="courses[courses.selected].length == 0 && courses.selected == 'past'" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursespast' | translate"></core-empty-box>
|
||||||
<ion-icon name="cloud-download"></ion-icon>
|
</div>
|
||||||
</button>
|
</core-loading>
|
||||||
</div>
|
</core-tab>
|
||||||
<div no-padding padding-bottom [hidden]="!showFilter">
|
</core-tabs>
|
||||||
<ion-item>
|
|
||||||
<ion-label><ion-icon name="funnel" class="placeholder-icon"></ion-icon></ion-label>
|
|
||||||
<ion-input type="text" name="filter" clearInput [(ngModel)]="courses.filter" (ngModelChange)="filterChanged($event)" [placeholder]="'core.courses.filtermycourses' | translate"></ion-input>
|
|
||||||
</ion-item>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ion-grid no-padding>
|
|
||||||
<ion-row no-padding>
|
|
||||||
<ion-col *ngFor="let course of filteredCourses" no-padding col-12 col-sm-6 col-md-6 col-lg-4 col-xl-4 align-self-stretch>
|
|
||||||
<core-courses-course-progress [course]="course" class="core-courseoverview"></core-courses-course-progress>
|
|
||||||
</ion-col>
|
|
||||||
</ion-row>
|
|
||||||
</ion-grid>
|
|
||||||
|
|
||||||
<core-empty-box *ngIf="courses[courses.selected].length == 0 && courses.selected == 'inprogress'" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursesinprogress' | translate"></core-empty-box>
|
|
||||||
<core-empty-box *ngIf="courses[courses.selected].length == 0 && courses.selected == 'future'" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursesfuture' | translate"></core-empty-box>
|
|
||||||
<core-empty-box *ngIf="courses[courses.selected].length == 0 && courses.selected == 'past'" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursespast' | translate"></core-empty-box>
|
|
||||||
</div>
|
|
||||||
</core-loading>
|
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
|
@ -51,6 +51,7 @@ export class CoreCoursesMyOverviewPage {
|
||||||
showFilter = false;
|
showFilter = false;
|
||||||
searchEnabled: boolean;
|
searchEnabled: boolean;
|
||||||
filteredCourses: any[];
|
filteredCourses: any[];
|
||||||
|
tabs = [];
|
||||||
|
|
||||||
protected prefetchIconInitialized = false;
|
protected prefetchIconInitialized = false;
|
||||||
protected myCoursesObserver;
|
protected myCoursesObserver;
|
||||||
|
@ -65,8 +66,6 @@ export class CoreCoursesMyOverviewPage {
|
||||||
ionViewDidLoad() {
|
ionViewDidLoad() {
|
||||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||||
|
|
||||||
this.switchTab(this.tabShown);
|
|
||||||
|
|
||||||
// @todo: Course download.
|
// @todo: Course download.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,11 +259,11 @@ export class CoreCoursesMyOverviewPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change tab being viewed.
|
* The tab has changed.
|
||||||
*
|
*
|
||||||
* @param {string} tab Tab to display.
|
* @param {string} tab Name of the new tab.
|
||||||
*/
|
*/
|
||||||
switchTab(tab: string) {
|
tabChanged(tab: string) {
|
||||||
this.tabShown = tab;
|
this.tabShown = tab;
|
||||||
switch (this.tabShown) {
|
switch (this.tabShown) {
|
||||||
case 'timeline':
|
case 'timeline':
|
||||||
|
|
Loading…
Reference in New Issue