forked from CIT/Vmeda.Online
		
	Merge pull request #1791 from crazyserver/MOBILE-2819
MOBILE-2819 a11y: Apply aria attributes to components
This commit is contained in:
		
						commit
						c11cfa336b
					
				| @ -7,7 +7,6 @@ $addon-mod-wiki-toc-background-color: $gray-light !default; | ||||
| ion-app.app-root addon-mod-wiki-index { | ||||
|     background-color: $white; | ||||
| 
 | ||||
|     .core-tabs-content-container, | ||||
|     .addon-mod_wiki-page-content { | ||||
|         background-color: $white; | ||||
|     } | ||||
|  | ||||
| @ -28,7 +28,7 @@ import { Component, Input, OnChanges, OnDestroy, Output, EventEmitter, SimpleCha | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-chrono', | ||||
|     template: '<span>{{ time / 1000 | coreSecondsToHMS }}</span>' | ||||
|     template: '<span role="timer">{{ time / 1000 | coreSecondsToHMS }}</span>' | ||||
| }) | ||||
| export class CoreChronoComponent implements OnChanges, OnDestroy { | ||||
|     @Input() running: boolean; // Set it to true to start the chrono. Set it to false to stop it.
 | ||||
|  | ||||
| @ -26,12 +26,14 @@ import { CoreLoggerProvider } from '@providers/logger'; | ||||
| }) | ||||
| export class CoreContextMenuPopoverComponent { | ||||
|     title: string; | ||||
|     uniqueId: string; | ||||
|     items: CoreContextMenuItemComponent[]; | ||||
|     protected logger: any; | ||||
| 
 | ||||
|     constructor(navParams: NavParams, private viewCtrl: ViewController, logger: CoreLoggerProvider) { | ||||
|         this.title = navParams.get('title'); | ||||
|         this.items = navParams.get('items') || []; | ||||
|         this.uniqueId = navParams.get('id'); | ||||
|         this.logger = logger.getInstance('CoreContextMenuPopoverComponent'); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -16,6 +16,7 @@ import { Component, Input, OnInit, OnDestroy, ElementRef, Optional } from '@angu | ||||
| import { PopoverController } from 'ionic-angular'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||
| import { CoreContextMenuItemComponent } from './context-menu-item'; | ||||
| import { CoreContextMenuPopoverComponent } from './context-menu-popover'; | ||||
| import { CoreTabComponent } from '@components/tabs/tab'; | ||||
| @ -34,14 +35,16 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     hideMenu = true; // It will be unhidden when items are added.
 | ||||
|     ariaLabel: string; | ||||
|     expanded = false; | ||||
|     protected items: CoreContextMenuItemComponent[] = []; | ||||
|     protected itemsMovedToParent: CoreContextMenuItemComponent[] = []; | ||||
|     protected itemsChangedStream: Subject<void>; // Stream to update the hideMenu boolean when items change.
 | ||||
|     protected instanceId: string; | ||||
|     protected parentContextMenu: CoreContextMenuComponent; | ||||
|     protected uniqueId: string; | ||||
| 
 | ||||
|     constructor(private translate: TranslateService, private popoverCtrl: PopoverController, elementRef: ElementRef, | ||||
|             private domUtils: CoreDomUtilsProvider, @Optional() public coreTab: CoreTabComponent) { | ||||
|             private domUtils: CoreDomUtilsProvider, @Optional() public coreTab: CoreTabComponent, utils: CoreUtilsProvider) { | ||||
|         // Create the stream and subscribe to it. We ignore successive changes during 250ms.
 | ||||
|         this.itemsChangedStream = new Subject<void>(); | ||||
|         this.itemsChangedStream.auditTime(250).subscribe(() => { | ||||
| @ -56,6 +59,9 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         // Calculate the unique ID.
 | ||||
|         this.uniqueId = 'core-context-menu-' + utils.getUniqueId('CoreContextMenuComponent'); | ||||
| 
 | ||||
|         this.instanceId = this.domUtils.storeInstanceByElement(elementRef.nativeElement, this); | ||||
|     } | ||||
| 
 | ||||
| @ -170,10 +176,19 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { | ||||
|      * @param {MouseEvent} event Event. | ||||
|      */ | ||||
|     showContextMenu(event: MouseEvent): void { | ||||
|         const popover = this.popoverCtrl.create(CoreContextMenuPopoverComponent, { title: this.title, items: this.items }); | ||||
|         if (!this.expanded) { | ||||
|             const popover = this.popoverCtrl.create(CoreContextMenuPopoverComponent, | ||||
|                 { title: this.title, items: this.items, id: this.uniqueId }); | ||||
| 
 | ||||
|             popover.onDidDismiss(() => { | ||||
|                 this.expanded = false; | ||||
|             }); | ||||
|             popover.present({ | ||||
|                 ev: event | ||||
|             }); | ||||
| 
 | ||||
|             this.expanded = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <ion-list> | ||||
| <ion-list [id]="uniqueId" role="menu"> | ||||
|     <ion-list-header *ngIf="title">{{title}}</ion-list-header> | ||||
|     <a ion-item text-wrap *ngFor="let item of items" core-link [capture]="item.captureLink" [autoLogin]="item.autoLogin" [href]="item.href" (click)="itemClicked($event, item)" [attr.aria-label]="item.ariaAction" [hidden]="item.hidden" [attr.detail-none]="!item.href || item.iconAction"> | ||||
|     <a ion-item text-wrap *ngFor="let item of items" core-link [capture]="item.captureLink" [autoLogin]="item.autoLogin" [href]="item.href" (click)="itemClicked($event, item)" [attr.aria-label]="item.ariaAction" [hidden]="item.hidden" [attr.detail-none]="!item.href || item.iconAction" role="menuitem" [attr.aria-controls]="uniqueId"> | ||||
|         <core-icon *ngIf="item.iconDescription" [name]="item.iconDescription" [attr.aria-label]="item.ariaDescription" item-start></core-icon> | ||||
|         <core-format-text [clean]="true" [text]="item.content"></core-format-text> | ||||
|         <core-icon *ngIf="(item.href || item.action) && item.iconAction && item.iconAction != 'spinner'" [name]="item.iconAction" item-end></core-icon> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <button [hidden]="hideMenu" ion-button clear icon-only [attr.aria-label]="ariaLabel" (click)="showContextMenu($event)" class="bar-button"> | ||||
| <button [hidden]="hideMenu" ion-button clear icon-only [attr.aria-label]="ariaLabel" (click)="showContextMenu($event)" class="bar-button" aria-haspopup="true" [attr.aria-expanded]="expanded" [attr.aria-controls]="uniqueId"> | ||||
|     <core-icon [name]="icon"></core-icon> | ||||
| </button> | ||||
| <ng-content></ng-content> | ||||
| @ -1 +1 @@ | ||||
| <ion-icon [name]="name" [isActive]="isActive" [md]="md" [ios]="ios" [color]="color"></ion-icon> | ||||
| <ion-icon [name]="name" [isActive]="isActive" [md]="md" [ios]="ios" [color]="color" aria-hidden="true" role="presentation"></ion-icon> | ||||
| @ -54,6 +54,8 @@ export class CoreIconComponent implements OnInit, OnDestroy { | ||||
|             this.newElement.classList.add('icon'); | ||||
|             this.newElement.classList.add('fa'); | ||||
|             this.newElement.classList.add(this.name); | ||||
|             this.newElement.setAttribute('aria-hidden', 'true'); | ||||
|             this.newElement.setAttribute('role', 'img'); | ||||
|             if (this.isTrueProperty(this.fixedWidth)) { | ||||
|                 this.newElement.classList.add('fa-fw'); | ||||
|             } | ||||
|  | ||||
| @ -12,5 +12,5 @@ | ||||
|             </div> | ||||
|         </ng-container> | ||||
|     </ng-container> | ||||
|     <div *ngIf="errorText" class="core-input-error">{{ errorText }}</div> | ||||
|     <div *ngIf="errorText" class="core-input-error" aria-live="assertive">{{ errorText }}</div> | ||||
| </div> | ||||
| @ -1,5 +1,5 @@ | ||||
| <div class="tabbar" role="tablist" #tabbar [hidden]="hidden"> | ||||
|     <a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t)"></a> | ||||
| <div class="tabbar" role="tablist" #tabbar> | ||||
|     <a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t)" [attr.aria-hidden]="!t.show" [attr.aria-label]="t.tabTitle || ''"></a> | ||||
|     <div class="tab-highlight"></div> | ||||
|     <div *ngIf="_loaded === false" class="core-ion-tabs-loading"> | ||||
|         <span class="core-ion-tabs-loading-spinner"> | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| <div [@coreShowHideAnimation] class="core-loading-container" *ngIf="!hideUntil"> | ||||
| <div [@coreShowHideAnimation] class="core-loading-container" *ngIf="!hideUntil" role="status"> | ||||
|     <span class="core-loading-spinner"> | ||||
|         <ion-spinner></ion-spinner> | ||||
|         <p class="core-loading-message" *ngIf="message">{{message}}</p> | ||||
|         <p class="core-loading-message" *ngIf="message" role="status">{{message}}</p> | ||||
|     </span> | ||||
| </div> | ||||
| <div #content class="core-loading-content" [id]="uniqueId"> | ||||
| <div #content class="core-loading-content" [id]="uniqueId" [attr.aria-busy]="hideUntil"> | ||||
|     <ng-content [@coreShowHideAnimation] *ngIf="hideUntil"> | ||||
|     </ng-content> | ||||
| </div> | ||||
| @ -1,5 +1,5 @@ | ||||
| <div *ngIf="progress >= 0"> | ||||
|     <progress max="100" [value]="progress"> | ||||
|     <progress max="100" [value]="progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" [attr.aria-valuenow]="progress"> | ||||
|         <div class="progress-bar-fallback" role="progressbar" aria-valuemin="0" aria-valuemax="100" [attr.aria-valuenow]="progress"> | ||||
|             <span [style.width]="width"></span> | ||||
|         </div> | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <div [hidden]="!rteEnabled"> | ||||
|     <div #editor contenteditable="true" class="core-rte-editor" tappable [attr.data-placeholder-text]="placeholder"> | ||||
|     <div #editor contenteditable="true" class="core-rte-editor" tappable [attr.data-placeholder-text]="placeholder" role="textbox"> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand --> | ||||
| @ -22,7 +22,7 @@ | ||||
| </div> | ||||
| 
 | ||||
| <div [hidden]="rteEnabled"> | ||||
|     <ion-textarea #textarea class="core-textarea" [placeholder]="placeholder" [attr.name]="name" ngControl="control" (ionChange)="onChange($event)"></ion-textarea> | ||||
|     <ion-textarea #textarea class="core-textarea" [placeholder]="placeholder" [attr.name]="name" ngControl="control" (ionChange)="onChange($event)" role="textbox"></ion-textarea> | ||||
|     <div class="core-rte-toolbar" [hidden]="!editorSupported"> | ||||
|         <div #decorate class="core-rte-buttons"> | ||||
|             <button tappable [core-suppress-events] (onClick)="toggleEditor($event)"><core-icon name="fa-pencil-square-o"></core-icon> {{ 'core.vieweditor' | translate }}</button> | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <ion-card> | ||||
|     <form #f="ngForm" (ngSubmit)="submitForm($event)"> | ||||
|     <form #f="ngForm" (ngSubmit)="submitForm($event)" role="search"> | ||||
|         <ion-item> | ||||
|             <ion-input type="text" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus" [disabled]="disabled"></ion-input> | ||||
|             <ion-input type="search" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus" [disabled]="disabled" role="searchbox"></ion-input> | ||||
|             <button item-end ion-button clear icon-only type="submit" class="button-small" [attr.aria-label]="searchLabel" [disabled]="!searchText || (searchText.length < lengthCheck)" [disabled]="disabled"> | ||||
|                 <ion-icon name="search"></ion-icon> | ||||
|             </button> | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <form> | ||||
|     <textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keydown.enter)="enterClicked($event)" (keydown.control.enter)="enterClicked($event, 'control')" (keydown.meta.enter)="enterClicked($event, 'meta')"></textarea> | ||||
|     <textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keydown.enter)="enterClicked($event)" (keydown.control.enter)="enterClicked($event, 'control')" (keydown.meta.enter)="enterClicked($event, 'meta')" aria-multiline="true"></textarea> | ||||
|     <ion-buttons end> | ||||
|         <button ion-button icon-only clear="true" type="submit" [disabled]="!message" [attr.aria-label]="'core.send' | translate" [core-suppress-events] (onClick)="submitForm($event)"> | ||||
|             <ion-icon name="send" color="dark"></ion-icon> | ||||
|  | ||||
| @ -1,23 +1,21 @@ | ||||
| <core-loading [hideUntil]="hideUntil" class="core-loading-center"> | ||||
|     <div class="core-tabs-bar" #topTabs [hidden]="!tabs || numTabsShown < 2"> | ||||
|     <div class="core-tabs-bar" #topTabs [hidden]="!tabs || numTabsShown < 2" id="core-tabs-bar"> | ||||
|         <ion-row> | ||||
|             <ion-col class="col-with-arrow" (click)="slidePrev()" no-padding col-1> | ||||
|             <ion-col class="col-with-arrow" (click)="slidePrev()" no-padding col-1 aria-hidden="true"> | ||||
|                 <ion-icon *ngIf="showPrevButton" name="arrow-back" md="ios-arrow-back"></ion-icon> | ||||
|             </ion-col> | ||||
|             <ion-col no-padding col-10> | ||||
|                 <ion-slides (ionSlideDidChange)="slideChanged()" [slidesPerView]="slidesShown" [dir]="direction"> | ||||
|                 <ion-slides (ionSlideDidChange)="slideChanged()" [slidesPerView]="slidesShown" [dir]="direction" role="tablist" [attr.aria-label]="description" aria-hidden="false"> | ||||
|                     <ng-container *ngFor="let tab of tabs; let idx = index"> | ||||
|                         <ion-slide *ngIf="tab.show"> | ||||
|                             <a [attr.aria-selected]="selected == idx" (click)="selectTab(idx)" class="tab-slide"> | ||||
|                        <ion-slide *ngIf="tab.show" [attr.aria-selected]="selected == idx" (click)="selectTab(idx)" class="tab-slide" [attr.aria-label]="tab.title || ''" role="tab" [attr.aria-controls]="tab.id" [id]="tab.id + '-tab'" [tabindex]="selected == idx ? null : -1"> | ||||
|                             <core-icon *ngIf="tab.icon" [name]="tab.icon"></core-icon> | ||||
|                                 <span *ngIf="tab.title">{{ tab.title }}</span> | ||||
|                             <div *ngIf="tab.title">{{ tab.title }}</div> | ||||
|                             <ion-badge *ngIf="tab.badge" [color]="tab.badgeStyle" class="tab-badge">{{tab.badge}}</ion-badge> | ||||
|                             </a> | ||||
|                         </ion-slide> | ||||
|                     </ng-container> | ||||
|                 </ion-slides> | ||||
|             </ion-col> | ||||
|             <ion-col class="col-with-arrow" (click)="slideNext()" no-padding col-1> | ||||
|             <ion-col class="col-with-arrow" (click)="slideNext()" no-padding col-1 aria-hidden="true"> | ||||
|                 <ion-icon *ngIf="showNextButton" name="arrow-forward" md="ios-arrow-forward"></ion-icon> | ||||
|             </ion-col> | ||||
|         </ion-row> | ||||
|  | ||||
| @ -16,6 +16,7 @@ import { Component, Input, Output, OnInit, OnDestroy, ElementRef, EventEmitter, | ||||
| import { CoreTabsComponent } from './tabs'; | ||||
| import { Content } from 'ionic-angular'; | ||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||
| import { CoreNavBarButtonsComponent } from '../navbar-buttons/navbar-buttons'; | ||||
| 
 | ||||
| /** | ||||
| @ -54,6 +55,9 @@ export class CoreTabComponent implements OnInit, OnDestroy { | ||||
| 
 | ||||
|             if (this.initialized && hasChanged) { | ||||
|                 this.tabs.tabVisibilityChanged(); | ||||
| 
 | ||||
|                 this.tabElement = document.getElementById(this.id + '-tab'); | ||||
|                 this.tabElement && this.tabElement.setAttribute('aria-hidden', !this._show); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -70,17 +74,31 @@ export class CoreTabComponent implements OnInit, OnDestroy { | ||||
|     loaded = false; | ||||
|     initialized = false; | ||||
|     _show = true; | ||||
|     tabElement: any; | ||||
| 
 | ||||
|     constructor(protected tabs: CoreTabsComponent, element: ElementRef, protected domUtils: CoreDomUtilsProvider) { | ||||
|     constructor(protected tabs: CoreTabsComponent, element: ElementRef, protected domUtils: CoreDomUtilsProvider, | ||||
|             utils: CoreUtilsProvider) { | ||||
|         this.element = element.nativeElement; | ||||
| 
 | ||||
|         this.element.setAttribute('role', 'tabpanel'); | ||||
|         this.element.setAttribute('tabindex', '0'); | ||||
|         this.id = this.id || 'core-tab-' + utils.getUniqueId('CoreTabComponent'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.element.setAttribute('aria-labelledby', this.id + '-tab'); | ||||
|         this.element.setAttribute('id', this.id); | ||||
| 
 | ||||
|         this.tabs.addTab(this); | ||||
|         this.initialized = true; | ||||
| 
 | ||||
|         setTimeout(() => { | ||||
|             this.tabElement = document.getElementById(this.id + '-tab'); | ||||
|             this.tabElement && this.tabElement.setAttribute('aria-hidden', !this._show); | ||||
|         }, 1000); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -96,6 +114,11 @@ export class CoreTabComponent implements OnInit, OnDestroy { | ||||
|     selectTab(): void { | ||||
|         this.element.classList.add('selected'); | ||||
| 
 | ||||
|         this.tabElement = this.tabElement || document.getElementById(this.id + '-tab'); | ||||
| 
 | ||||
|         this.tabElement && this.tabElement.setAttribute('aria-selected', true); | ||||
|         this.tabElement && this.tabElement.setAttribute('aria-hidden', !this._show); | ||||
| 
 | ||||
|         this.loaded = true; | ||||
|         this.ionSelect.emit(this); | ||||
|         this.showHideNavBarButtons(true); | ||||
| @ -114,6 +137,9 @@ export class CoreTabComponent implements OnInit, OnDestroy { | ||||
|      * Unselect tab. | ||||
|      */ | ||||
|     unselectTab(): void { | ||||
|         this.tabElement && this.tabElement.setAttribute('aria-selected', false); | ||||
|         this.tabElement && this.tabElement.setAttribute('aria-hidden', true); | ||||
| 
 | ||||
|         this.element.classList.remove('selected'); | ||||
|         this.showHideNavBarButtons(false); | ||||
|     } | ||||
|  | ||||
| @ -10,19 +10,20 @@ ion-app.app-root .core-tabs-bar { | ||||
|         width: 100%; | ||||
|     } | ||||
| 
 | ||||
|     a.tab-slide { | ||||
|     ion-slide.tab-slide { | ||||
|         @extend .tab-button; | ||||
| 
 | ||||
|         background: $core-top-tabs-background; | ||||
|         color: $core-top-tabs-color !important; | ||||
|         font-size: 1.6rem; | ||||
|         border: 0; | ||||
|         padding: 0 5px 0 5px !important; | ||||
|         border-bottom: 2px solid transparent !important; | ||||
|         padding: 0 2px 0 2px !important; | ||||
|         margin: 0 auto !important; | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         flex: none; | ||||
|         min-width: 100px; | ||||
| 
 | ||||
|         span { | ||||
|         div { | ||||
|             text-overflow: ellipsis; | ||||
|             white-space: nowrap; | ||||
|             overflow: hidden; | ||||
| @ -44,9 +45,12 @@ ion-app.app-root .core-tabs-bar { | ||||
| 
 | ||||
|         &[aria-selected=true] { | ||||
|             color: $core-top-tabs-color-active !important; | ||||
|             border: 0 !important; | ||||
|             border-bottom: 2px solid $core-top-tabs-border-active !important; | ||||
|             padding: 0 !important; | ||||
|             border-bottom-color: $core-top-tabs-border-active !important; | ||||
|         } | ||||
| 
 | ||||
|         .slide-zoom { | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -64,7 +68,7 @@ ion-app.app-root .core-tabs-bar { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ion-app.app-root.md .core-tabs-bar a.tab-slide { | ||||
| ion-app.app-root.md .core-tabs-bar ion-slide.tab-slide { | ||||
|     // @extend .tabs-md .tab-button; | ||||
|     min-height: $tabs-md-tab-min-height; | ||||
| 
 | ||||
| @ -72,7 +76,7 @@ ion-app.app-root.md .core-tabs-bar a.tab-slide { | ||||
|     color: $tabs-md-tab-text-color; | ||||
| } | ||||
| 
 | ||||
| ion-app.app-root.ios .core-tabs-bar a.tab-slide { | ||||
| ion-app.app-root.ios .core-tabs-bar ion-slide.tab-slide { | ||||
|     // @extend .tabs-ios .tab-button; | ||||
|     max-width: $tabs-ios-tab-max-width; | ||||
|     min-height: $tabs-ios-tab-min-height; | ||||
| @ -82,7 +86,7 @@ ion-app.app-root.ios .core-tabs-bar a.tab-slide { | ||||
|     color: $tabs-ios-tab-text-color; | ||||
| } | ||||
| 
 | ||||
| ion-app.app-root.wp .core-tabs-bar a.tab-slide { | ||||
| ion-app.app-root.wp .core-tabs-bar ion-slide.tab-slide { | ||||
|     //@extend .tabs-wp .tab-button; | ||||
|     @include border-radius(0); | ||||
| 
 | ||||
| @ -124,6 +128,7 @@ ion-app.app-root core-tabs { | ||||
|         display: none; | ||||
|         height: 100%; | ||||
|         position: relative; | ||||
|         z-index: 1; | ||||
| 
 | ||||
|         &.selected { | ||||
|             display: block; | ||||
|  | ||||
| @ -62,6 +62,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges, OnDe | ||||
|     slidesShown = this.maxSlides; | ||||
|     numTabsShown = 0; | ||||
|     direction = 'ltr'; | ||||
|     description = ''; | ||||
| 
 | ||||
|     protected originalTabsContainer: HTMLElement; // The container of the original tabs. It will include each tab's content.
 | ||||
|     protected initialized = false; | ||||
| @ -238,8 +239,8 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges, OnDe | ||||
|     /** | ||||
|      * Get the index of tab. | ||||
|      * | ||||
|      * @param  {any}    tab [description] | ||||
|      * @return {number}     [description] | ||||
|      * @param  {any}    tab Tab object to check. | ||||
|      * @return {number}     Index number on the tabs array or -1 if not found. | ||||
|      */ | ||||
|     getIndex(tab: any): number { | ||||
|         for (let i = 0; i < this.tabs.length; i++) { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <ion-item #container class="core-timer" [attr.text-center]="align == 'center' ? true : null" [attr.text-end]="align == 'right' ? true : null"> | ||||
|     <ion-icon name="timer" item-start></ion-icon> | ||||
| <ion-item #container class="core-timer" [attr.text-center]="align == 'center' ? true : null" [attr.text-end]="align == 'right' ? true : null" role="timer"> | ||||
|     <ion-icon name="timer" item-start role="presentation"></ion-icon> | ||||
|     <label *ngIf="timeLeft > 0 && timerText">{{ timerText }}</label> | ||||
|     <span *ngIf="timeLeft > 0">{{ timeLeft | coreSecondsToHMS }}</span> | ||||
|     <span class="core-timesup" *ngIf="timeLeft <= 0">{{ 'core.timesup' | translate }}</span> | ||||
|  | ||||
| @ -11,7 +11,7 @@ | ||||
|         <!-- Section selector. --> | ||||
|         <core-dynamic-component [component]="sectionSelectorComponent" [data]="data"> | ||||
|             <div text-wrap *ngIf="displaySectionSelector && sections && sections.length" padding class="clearfix" ion-row justify-content-between class="safe-padding-horizontal"> | ||||
|                 <button float-start ion-button icon-start icon-end (click)="showSectionSelector($event)" color="light" class="core-button-select button-no-uppercase" ion-col> | ||||
|                 <button float-start ion-button icon-start icon-end (click)="showSectionSelector($event)" color="light" class="core-button-select button-no-uppercase" ion-col [attr.aria-label]="('core.course.sections' | translate) + ': ' + (selectedSection && (selectedSection.formattedName || selectedSection.name))" aria-haspopup="true" [attr.aria-expanded]="sectionSelectorExpanded" aria-controls="core-course-section-selector" id="core-course-section-button"> | ||||
|                     <core-icon name="fa-folder"></core-icon> | ||||
|                     <span class="core-section-selector-text">{{selectedSection && (selectedSection.formattedName || selectedSection.name) || 'core.course.sections' | translate }}</span> | ||||
|                     <ion-icon name="arrow-dropdown" ios="md-arrow-dropdown"></ion-icon> | ||||
|  | ||||
| @ -61,6 +61,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|     allSectionsComponent: any; | ||||
|     canLoadMore = false; | ||||
|     showSectionId = 0; | ||||
|     sectionSelectorExpanded = false; | ||||
| 
 | ||||
|     // Data to pass to the components.
 | ||||
|     data: any = {}; | ||||
| @ -243,16 +244,27 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
| 
 | ||||
|     /** | ||||
|      * Display the section selector modal. | ||||
|      * | ||||
|      * @param {MouseEvent} event Event. | ||||
|      */ | ||||
|     showSectionSelector(): void { | ||||
|     showSectionSelector(event: MouseEvent): void { | ||||
|         if (!this.sectionSelectorExpanded) { | ||||
|             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 = false; | ||||
|             }); | ||||
|         modal.present(); | ||||
| 
 | ||||
|             modal.present({ | ||||
|                 ev: event | ||||
|             }); | ||||
| 
 | ||||
|             this.sectionSelectorExpanded = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <a *ngIf="module && module.visibleoncoursepage !== 0" ion-item text-wrap id="core-course-module-{{module.id}}" class="core-course-module-handler {{module.handlerData.class}}" (click)="moduleClicked($event)" [ngClass]="{'item-media': module.handlerData.icon, 'core-not-clickable': !module.handlerData.action || !module.uservisible === false, 'item-dimmed': module.visible === 0 || module.uservisible === false}" [title]="module.handlerData.a11yTitle" detail-none> | ||||
| 
 | ||||
|     <img item-start *ngIf="module.handlerData.icon" [src]="module.handlerData.icon" alt="" role="presentation" class="core-module-icon"> | ||||
|     <img item-start *ngIf="module.handlerData.icon" [src]="module.handlerData.icon" [alt]="module.modnametranslated" class="core-module-icon"> | ||||
|     <div class="core-module-title"> | ||||
|         <core-format-text [text]="module.handlerData.title"></core-format-text> | ||||
| 
 | ||||
|  | ||||
| @ -18,6 +18,7 @@ import { CoreEventsProvider } from '@providers/events'; | ||||
| import { CoreSitesProvider } from '@providers/sites'; | ||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||
| import { CoreCourseHelperProvider } from '../../providers/helper'; | ||||
| import { CoreCourseProvider } from '../../providers/course'; | ||||
| import { CoreCourseModuleHandlerButton } from '../../providers/module-delegate'; | ||||
| import { CoreCourseModulePrefetchDelegate, CoreCourseModulePrefetchHandler } from '../../providers/module-prefetch-delegate'; | ||||
| import { CoreConstants } from '../../../constants'; | ||||
| @ -63,7 +64,8 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     constructor(@Optional() protected navCtrl: NavController, protected prefetchDelegate: CoreCourseModulePrefetchDelegate, | ||||
|             protected domUtils: CoreDomUtilsProvider, protected courseHelper: CoreCourseHelperProvider, | ||||
|             protected eventsProvider: CoreEventsProvider, protected sitesProvider: CoreSitesProvider) { | ||||
|             protected eventsProvider: CoreEventsProvider, protected sitesProvider: CoreSitesProvider, | ||||
|             protected courseProvider: CoreCourseProvider) { | ||||
|         this.completionChanged = new EventEmitter(); | ||||
|     } | ||||
| 
 | ||||
| @ -97,6 +99,8 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { | ||||
| 
 | ||||
|         this.module.handlerData.a11yTitle = typeof this.module.handlerData.a11yTitle != 'undefined' ? | ||||
|             this.module.handlerData.a11yTitle : this.module.handlerData.title; | ||||
| 
 | ||||
|         this.module.modnametranslated = this.courseProvider.translateModuleName(this.module.modname) || ''; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -9,13 +9,15 @@ | ||||
|     </ion-navbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-list id="core-course-section-selector" role="menu" aria-labelledby="core-course-section-button"> | ||||
|         <ng-container *ngFor="let section of sections"> | ||||
|         <a ion-item *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" text-wrap (click)="selectSection(section)" [class.core-primary-selected-item]="selected.id == section.id" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail-none> | ||||
|             <a ion-item *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" text-wrap (click)="selectSection(section)" [class.core-primary-selected-item]="selected.id == section.id" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail-none role="menuitem" [attr.aria-hidden]="section.uservisible === false" [attr.aria-label]="section.formattedName || section.name"> | ||||
|                 <core-icon name="fa-folder" item-start></core-icon> | ||||
|                 <h2><core-format-text [text]="section.formattedName || section.name"></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">{{ 'core.course.hiddenfromstudents' | translate }}</ion-badge> | ||||
|             <ion-badge color="secondary" *ngIf="section.availabilityinfo"><core-format-text  [text]=" section.availabilityinfo"></core-format-text></ion-badge> | ||||
|                 <ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible !== false" text-wrap>{{ 'core.course.hiddenfromstudents' | translate }}</ion-badge> | ||||
|                 <ion-badge color="secondary" *ngIf="section.availabilityinfo" text-wrap><core-format-text  [text]=" section.availabilityinfo"></core-format-text></ion-badge> | ||||
|             </a> | ||||
|         </ng-container> | ||||
|     </ion-list> | ||||
| </ion-content> | ||||
|  | ||||
| @ -8,4 +8,8 @@ ion-app.app-root page-core-course-section-selector { | ||||
|             margin: 8px 0 4px 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ion-badge { | ||||
|         text-align: left; | ||||
|     } | ||||
| } | ||||
| @ -30,19 +30,19 @@ | ||||
|             </a> | ||||
|         </div> | ||||
|         <a *ngIf="showWeb" ion-item [href]="siteInfo.siteurl" core-link autoLogin="yes" title="{{ 'core.mainmenu.website' | translate }}"> | ||||
|             <ion-icon name="globe" item-start></ion-icon> | ||||
|             <ion-icon name="globe" item-start aria-hidden="true"></ion-icon> | ||||
|             <p>{{ 'core.mainmenu.website' | translate }}</p> | ||||
|         </a> | ||||
|         <a *ngIf="showHelp" ion-item [href]="docsUrl" core-link autoLogin="no" title="{{ 'core.mainmenu.help' | translate }}"> | ||||
|             <ion-icon name="help-buoy" item-start></ion-icon> | ||||
|             <ion-icon name="help-buoy" item-start aria-hidden="true"></ion-icon> | ||||
|             <p>{{ 'core.mainmenu.help' | translate }}</p> | ||||
|         </a> | ||||
|         <a ion-item (click)="openSettings()" title="{{ 'core.mainmenu.appsettings' | translate }}"> | ||||
|             <ion-icon name="cog" item-start></ion-icon> | ||||
|             <ion-icon name="cog" item-start aria-hidden="true"></ion-icon> | ||||
|             <p>{{ 'core.mainmenu.appsettings' | translate }}</p> | ||||
|         </a> | ||||
|         <a ion-item (click)="logout()" title="{{ logoutLabel | translate }}"> | ||||
|             <ion-icon name="log-out" item-start></ion-icon> | ||||
|             <ion-icon name="log-out" item-start aria-hidden="true"></ion-icon> | ||||
|             <p>{{ logoutLabel | translate }}</p> | ||||
|         </a> | ||||
|     </ion-list> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user