commit
dab503bb9e
|
@ -4319,11 +4319,11 @@
|
|||
}
|
||||
},
|
||||
"@ionic/angular": {
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.9.2.tgz",
|
||||
"integrity": "sha512-5GzKg+l4au3xFECky2v/USlRsmTAXgvNO5Zalt7NUXc//VJIL2lQvswojE6FBWuM/xR5W0CWbJdFth19TaZWVQ==",
|
||||
"version": "5.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.9.4.tgz",
|
||||
"integrity": "sha512-U/85FePF48VaZXTudTwpVXDqhGmYfarl/7vki7a4umnIORnWtHqD2/pXsqqZ/O1EcbALwULYIeVXAfkFpPd2wQ==",
|
||||
"requires": {
|
||||
"@ionic/core": "5.9.2",
|
||||
"@ionic/core": "5.9.4",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -4666,9 +4666,9 @@
|
|||
}
|
||||
},
|
||||
"@ionic/core": {
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.9.2.tgz",
|
||||
"integrity": "sha512-1ZqSBS8R6tGQsc+LsLxIRv0q3Ww6jwgJXLvdn6FmVWfpPbBvT+CjCuU9hqJ5qwM+atErblUMYSexvvpws8lGAA==",
|
||||
"version": "5.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.9.4.tgz",
|
||||
"integrity": "sha512-Ngz9yVT6fIiGdSxxBer8uJxP4w6PasvohYpLxhtMgYiWnyIu0vZra2ui3HrYukCzUo5/SbNPiUr1l7cj1E+7qw==",
|
||||
"requires": {
|
||||
"@stencil/core": "^2.4.0",
|
||||
"ionicons": "^5.5.3",
|
||||
|
@ -5852,9 +5852,9 @@
|
|||
}
|
||||
},
|
||||
"@stencil/core": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.11.0.tgz",
|
||||
"integrity": "sha512-/IubCWhVXCguyMUp/3zGrg3c882+RJNg/zpiKfyfJL3kRCOwe+/MD8OoAXVGdd+xAohZKIi1Ik+EHFlsptsjLg=="
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.2.tgz",
|
||||
"integrity": "sha512-r+vbxsGNcBaV1VDOYW25lv4QfXTlNoIb5GpUX7rZ+cr59yqYCZC5tlV+IzX6YgHKW62ulCc9M3RYtTfHtNbNNw=="
|
||||
},
|
||||
"@storybook/addon-controls": {
|
||||
"version": "6.1.21",
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
"@ionic-native/status-bar": "5.36.0",
|
||||
"@ionic-native/web-intent": "5.36.0",
|
||||
"@ionic-native/zip": "5.36.0",
|
||||
"@ionic/angular": "5.9.2",
|
||||
"@ionic/angular": "5.9.4",
|
||||
"@moodlehq/cordova-plugin-file-opener": "3.0.5-moodle.1",
|
||||
"@moodlehq/cordova-plugin-file-transfer": "1.7.1-moodle.5",
|
||||
"@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3",
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</ion-grid>
|
||||
|
||||
<core-swipe-slides [manager]="manager">
|
||||
<ng-template let-month="item">
|
||||
<ng-template let-month="item" let-activeView="active">
|
||||
<!-- Calendar view. -->
|
||||
<ion-grid class="addon-calendar-months" role="table" aria-describedby="addon-calendar-monthname">
|
||||
<div role="rowgroup">
|
||||
|
@ -57,9 +57,9 @@
|
|||
"today": month.isCurrentMonth && day.istoday,
|
||||
"weekend": day.isweekend,
|
||||
"duration_finish": day.haslastdayofevent
|
||||
}' [class.addon-calendar-event-past-day]="month.isPastMonth || day.ispast" role="cell" tabindex="0"
|
||||
(ariaButtonClick)="dayClicked(day.mday)">
|
||||
<p class="addon-calendar-day-number" role="button">
|
||||
}' [class.addon-calendar-event-past-day]="month.isPastMonth || day.ispast" role="cell"
|
||||
(ariaButtonClick)="dayClicked(day.mday)" [tabindex]="activeView ? 0 : -1">
|
||||
<p class="addon-calendar-day-number">
|
||||
<span aria-hidden="true">{{ day.mday }}</span>
|
||||
<span class="sr-only">{{ day.periodName | translate }}</span>
|
||||
</p>
|
||||
|
@ -72,8 +72,8 @@
|
|||
<div class="ion-hide-md-down addon-calendar-day-events" *ngIf="day.filteredEvents">
|
||||
<ng-container *ngFor="let event of day.filteredEvents | slice:0:4; let index = index">
|
||||
<div *ngIf="index < 3 || day.filteredEvents.length == 4" class="addon-calendar-event"
|
||||
[class.addon-calendar-event-past]="event.ispast" role="button" tabindex="0"
|
||||
(ariaButtonClick)="eventClicked(event, $event)">
|
||||
[class.addon-calendar-event-past]="event.ispast" (ariaButtonClick)="eventClicked(event, $event)"
|
||||
[tabindex]="activeView ? 0 : -1">
|
||||
<span class="calendar_event_type calendar_event_{{event.formattedType}}"></span>
|
||||
<ion-icon *ngIf="event.offline && !event.deleted" name="fas-clock"
|
||||
[attr.aria-label]="'core.notsent' | translate"></ion-icon>
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
@include border-end(1px, solid var(--addon-calendar-border-color));
|
||||
overflow: hidden;
|
||||
min-height: 60px;
|
||||
cursor: pointer;
|
||||
|
||||
&:first-child {
|
||||
@include padding-horizontal(10px, null);
|
||||
|
|
|
@ -74,26 +74,20 @@
|
|||
[lines]="discussion.groupname && 'none'" [attr.aria-current]="discussions?.getItemAriaCurrent(discussion)"
|
||||
(click)="discussions?.select(discussion)" button>
|
||||
<ion-label>
|
||||
<div class="addon-mod-forum-discussion-title">
|
||||
<p class="ion-text-wrap item-heading">
|
||||
<ion-icon name="fas-map-pin" *ngIf="discussion.pinned"
|
||||
[attr.aria-label]="'addon.mod_forum.discussionpinned' | translate"></ion-icon>
|
||||
<ion-icon name="fas-star" class="addon-forum-star" *ngIf="!discussion.pinned && discussion.starred"
|
||||
[attr.aria-label]="'addon.mod_forum.favourites' | translate"></ion-icon>
|
||||
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module && module.id"
|
||||
[courseId]="courseId">
|
||||
</core-format-text>
|
||||
<ion-icon name="fas-lock" *ngIf="discussion.locked" class="addon-mod-forum-locked-icon"
|
||||
[attr.aria-label]="'addon.mod_forum.discussionlocked' | translate"></ion-icon>
|
||||
</p>
|
||||
<ion-button *ngIf="canPin || discussion.canlock || discussion.canfavourite" fill="clear"
|
||||
[attr.aria-label]="('core.displayoptions' | translate)" (click)="showOptionsMenu($event, discussion)">
|
||||
<ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true">
|
||||
</ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
<p class="addon-mod-forum-discussion-title ion-text-wrap item-heading">
|
||||
<ion-icon name="fas-map-pin" *ngIf="discussion.pinned"
|
||||
[attr.aria-label]="'addon.mod_forum.discussionpinned' | translate"></ion-icon>
|
||||
<ion-icon name="fas-star" class="addon-forum-star" *ngIf="!discussion.pinned && discussion.starred"
|
||||
[attr.aria-label]="'addon.mod_forum.favourites' | translate"></ion-icon>
|
||||
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module && module.id"
|
||||
[courseId]="courseId">
|
||||
</core-format-text>
|
||||
<ion-icon name="fas-lock" *ngIf="discussion.locked" class="addon-mod-forum-locked-icon"
|
||||
[attr.aria-label]="'addon.mod_forum.discussionlocked' | translate"></ion-icon>
|
||||
</p>
|
||||
<div class="addon-mod-forum-discussion-info">
|
||||
<core-user-avatar *ngIf="discussion.userfullname" [user]="discussion" slot="start" [courseId]="courseId">
|
||||
<core-user-avatar *ngIf="discussion.userfullname" [user]="discussion" slot="start" [courseId]="courseId"
|
||||
[linkProfile]="false">
|
||||
</core-user-avatar>
|
||||
<div class="addon-mod-forum-discussion-author">
|
||||
<span *ngIf="discussion.userfullname">{{discussion.userfullname}}</span>
|
||||
|
@ -136,6 +130,11 @@
|
|||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-label>
|
||||
<ion-button *ngIf="canPin || discussion.canlock || discussion.canfavourite" fill="clear"
|
||||
[attr.aria-label]="('core.displayoptions' | translate)" (click)="showOptionsMenu($event, discussion)" slot="end">
|
||||
<ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true">
|
||||
</ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
|
||||
<core-infinite-loading [enabled]="discussions && discussions.loaded && !discussions.completed" [error]="fetchFailed"
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
}
|
||||
|
||||
.addon-mod-forum-discussion.item {
|
||||
|
||||
ion-label {
|
||||
margin-top: 4px;
|
||||
|
||||
|
@ -35,21 +34,30 @@
|
|||
@include margin(0, 8px, 0, 0);
|
||||
}
|
||||
|
||||
.addon-mod-forum-discussion-title,
|
||||
.addon-mod-forum-discussion-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.addon-mod-forum-discussion-title .item-heading,
|
||||
.addon-mod-forum-discussion-info .addon-mod-forum-discussion-author {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.addon-mod-forum-discussion-title {
|
||||
@include margin-horizontal(null, 8px);
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
|
||||
.addon-mod-forum-discussion-more-info.ios {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
ion-button {
|
||||
position: absolute;
|
||||
@include position (4px, 8px, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.core-group-selector {
|
||||
|
|
|
@ -29,19 +29,19 @@ export class CoreAriaRoleTab<T = unknown> {
|
|||
* @param e Event.
|
||||
*/
|
||||
keyDown(tabFindIndex: string, e: KeyboardEvent): void {
|
||||
if (e.key == ' ' ||
|
||||
e.key == 'Enter' ||
|
||||
e.key == 'Home' ||
|
||||
e.key == 'End' ||
|
||||
(this.isHorizontal() && (e.key == 'ArrowRight' || e.key == 'ArrowLeft')) ||
|
||||
(!this.isHorizontal() && (e.key == 'ArrowUp' ||e.key == 'ArrowDown'))
|
||||
if (e.key === ' ' ||
|
||||
e.key === 'Enter' ||
|
||||
e.key === 'Home' ||
|
||||
e.key === 'End' ||
|
||||
(this.isHorizontal() && (e.key === 'ArrowRight' || e.key === 'ArrowLeft')) ||
|
||||
(!this.isHorizontal() && (e.key === 'ArrowUp' ||e.key === 'ArrowDown'))
|
||||
) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
if (e.key == ' ' || e.key == 'Enter') {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
this.selectTabCandidate = tabFindIndex;
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ export class CoreAriaRoleTab<T = unknown> {
|
|||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
if (e.key == ' ' || e.key == 'Enter') {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
if (this.selectTabCandidate === tabFindIndex) {
|
||||
this.selectTab(tabFindIndex, e);
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
|
|||
* @param event The mouse event.
|
||||
*/
|
||||
doNotBlur(event: Event): void {
|
||||
if (event.type == 'keydown' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
|
||||
if (event.type === 'keydown' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
|
|||
* @returns Wether space or enter have been pressed.
|
||||
*/
|
||||
protected isValidKeyboardKey(event: KeyboardEvent): boolean {
|
||||
return event.key == ' ' || event.key == 'Enter';
|
||||
return event.key === ' ' || event.key === 'Enter';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<ion-slides *ngIf="loaded" (ionSlideWillChange)="slideWillChange()" (ionSlideDidChange)="slideDidChange()" [options]="options">
|
||||
<ion-slide *ngFor="let item of items; index as index">
|
||||
<ion-slide *ngFor="let item of items; index as index" [attr.aria-hidden]="!isActive(index)">
|
||||
<ng-container *ngIf="template" [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{item: item, active: isActive(index)}">
|
||||
</ng-container>
|
||||
</ion-slide>
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<img *ngIf="avatarUrl" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
|
||||
onError="this.src='assets/img/user-avatar.png'" (ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile"
|
||||
[attr.role]="linkProfile ? 'button' : null" [attr.tabindex]="linkProfile ? 0 : null" [class.clickable]="linkProfile">
|
||||
<img *ngIf="avatarUrl && linkProfile" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
|
||||
onError="this.src='assets/img/user-avatar.png'" (ariaButtonClick)="gotoProfile($event)">
|
||||
|
||||
<img *ngIf="!avatarUrl" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
|
||||
(ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" [attr.role]="linkProfile ? 'button' : null"
|
||||
[attr.tabindex]="linkProfile ? 0 : null">
|
||||
<img *ngIf="avatarUrl && !linkProfile" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
|
||||
onError="this.src='assets/img/user-avatar.png'" aria-hidden="true">
|
||||
|
||||
<img *ngIf="!avatarUrl && linkProfile" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
|
||||
(ariaButtonClick)="gotoProfile($event)">
|
||||
|
||||
<img *ngIf="!avatarUrl && !linkProfile" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
|
||||
aria-hidden="true">
|
||||
|
||||
<span *ngIf="checkOnline && isOnline()" class="contact-status online" role="status" [attr.aria-label]="'core.online' | translate">
|
||||
</span>
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
width: var(--core-avatar-size);
|
||||
height: var(--core-avatar-size);
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
img {
|
||||
border-radius: 50%;
|
||||
width: var(--core-avatar-size);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Directive, ElementRef, OnInit, Output, EventEmitter } from '@angular/core';
|
||||
import { Directive, ElementRef, OnInit, Output, EventEmitter, OnChanges, SimpleChanges, Input } from '@angular/core';
|
||||
import { CoreDom } from '@singletons/dom';
|
||||
|
||||
/**
|
||||
|
@ -21,10 +21,11 @@ import { CoreDom } from '@singletons/dom';
|
|||
@Directive({
|
||||
selector: '[ariaButtonClick]',
|
||||
})
|
||||
export class CoreAriaButtonClickDirective implements OnInit {
|
||||
export class CoreAriaButtonClickDirective implements OnInit, OnChanges {
|
||||
|
||||
protected element: HTMLElement;
|
||||
|
||||
@Input() disabled = false;
|
||||
@Output() ariaButtonClick = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
|
@ -34,10 +35,27 @@ export class CoreAriaButtonClickDirective implements OnInit {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize actions.
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
CoreDom.onActivate(this.element, (event) => this.ariaButtonClick.emit(event));
|
||||
CoreDom.initializeClickableElementA11y(this.element, (event) => this.ariaButtonClick.emit(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (!changes.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.element.getAttribute('tabindex') === '0' && this.disabled) {
|
||||
this.element.setAttribute('tabindex', '-1');
|
||||
}
|
||||
|
||||
if (this.element.getAttribute('tabindex') === '-1' && !this.disabled) {
|
||||
this.element.setAttribute('tabindex', '0');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -610,12 +610,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncCompo
|
|||
return;
|
||||
}
|
||||
|
||||
if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
|
||||
element.setAttribute('tabindex', '0');
|
||||
element.setAttribute('role', 'button');
|
||||
}
|
||||
|
||||
CoreDom.onActivate(element, async (event) => {
|
||||
CoreDom.initializeClickableElementA11y(element, async (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
|
|
|
@ -57,12 +57,7 @@ export class CoreLinkDirective implements OnInit {
|
|||
* Function executed when the component is initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
if (this.element.tagName != 'BUTTON' && this.element.tagName != 'A') {
|
||||
this.element.setAttribute('tabindex', '0');
|
||||
this.element.setAttribute('role', 'button');
|
||||
}
|
||||
|
||||
CoreDom.onActivate(this.element, (event) => this.performAction(event));
|
||||
CoreDom.initializeClickableElementA11y(this.element, (event) => this.performAction(event));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
class="expandable-status-icon" (ariaButtonClick)="toggleExpand($event, section)"
|
||||
[attr.aria-label]="(section.expanded ? 'core.collapse' : 'core.expand') | translate"
|
||||
[attr.aria-expanded]="section.expanded" [attr.aria-controls]="'core-course-index-section-' + section.id"
|
||||
[class.expandable-status-icon-expanded]="section.expanded" tabindex="0">
|
||||
[class.expandable-status-icon-expanded]="section.expanded">
|
||||
</ion-icon>
|
||||
<ion-icon *ngIf="!section.hasVisibleModules" name="" slot="start" aria-hidden="true" class="expandable-status-icon">
|
||||
</ion-icon>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0" [class.core-course-module-with-view]="moduleHasView">
|
||||
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0" [class.core-course-module-with-view]="moduleHasView"
|
||||
(click)="moduleClicked($event)" [button]="module.handlerData.action && module.uservisible"
|
||||
[attr.aria-label]="module.handlerData.a11yTitle ? module.handlerData.a11yTitle : null">
|
||||
<ng-container *ngIf="!module.handlerData.loading">
|
||||
<ion-item id="core-course-module-{{module.id}}" detail="false"
|
||||
class="ion-text-wrap core-course-module-handler core-module-main-item {{module.handlerData.class}}"
|
||||
(click)="moduleClicked($event)" [attr.aria-label]="module.handlerData.a11yTitle" [ngClass]="{
|
||||
<ion-item id="core-course-module-{{module.id}}"
|
||||
class="ion-text-wrap core-course-module-handler core-module-main-item {{module.handlerData.class}}" [ngClass]="{
|
||||
'item-dimmed': module.visible === 0 || module.uservisible === false
|
||||
}" [button]="module.handlerData.action && module.uservisible">
|
||||
}">
|
||||
|
||||
<core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname"
|
||||
[componentId]="module.instance">
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<ion-card [class.core-course-list-item]="layout == 'list' || layout == 'listwithenrol'"
|
||||
[class.core-course-list-card]="layout == 'card' || layout == 'summarycard'" [class.item-dimmed]="course.hidden">
|
||||
[class.core-course-list-card]="layout == 'card' || layout == 'summarycard'" [class.item-dimmed]="course.hidden" (click)="openCourse()"
|
||||
button [attr.aria-label]="course.displayname || course.fullname">
|
||||
|
||||
<div *ngIf="layout == 'card' || layout == 'summarycard'" (click)="openCourse()" class="core-course-thumb"
|
||||
[class.core-course-color-img]="course.courseImage">
|
||||
<div *ngIf="layout == 'card' || layout == 'summarycard'" class="core-course-thumb" [class.core-course-color-img]="course.courseImage">
|
||||
<img *ngIf="course.courseImage" [src]="course.courseImage" core-external-content alt="" />
|
||||
<ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" class="course-icon" aria-hidden="true">
|
||||
</ion-icon>
|
||||
|
@ -27,7 +27,7 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ion-item class="ion-text-wrap" button detail="false" (click)="openCourse()" [attr.aria-label]="course.displayname || course.fullname">
|
||||
<ion-item class="ion-text-wrap">
|
||||
|
||||
<ng-container *ngIf="layout == 'list' || layout == 'listwithenrol'">
|
||||
<ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" slot="start" class="course-icon core-course-thumb"
|
||||
|
|
|
@ -1,38 +1,40 @@
|
|||
<ion-item button class="ion-text-wrap" (click)="action('download')" *ngIf="downloadCourseEnabled" detail="false">
|
||||
<ion-icon *ngIf="!prefetch.loading" [name]="prefetch.icon" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-spinner *ngIf="prefetch.loading" slot="start" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
|
||||
<ion-label>
|
||||
<h2>{{ prefetch.statusTranslatable | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('delete')" detail="false"
|
||||
*ngIf="prefetch.status == 'downloaded' || prefetch.status == 'outdated'">
|
||||
<ion-icon name="fas-trash" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.storagemanager.deletedata' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('hide')" *ngIf="!course.hidden" detail="false">
|
||||
<ion-icon name="fas-eye" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.courses.hidecourse' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('show')" *ngIf="course.hidden" detail="false">
|
||||
<ion-icon name="fas-eye-slash" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.courses.show' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('favourite')" *ngIf="!course.isfavourite" detail="false">
|
||||
<ion-icon name="fas-star" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.courses.addtofavourites' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('unfavourite')" *ngIf="course.isfavourite" detail="false">
|
||||
<ion-icon name="far-star" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.courses.removefromfavourites' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-list>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('download')" *ngIf="downloadCourseEnabled" detail="false">
|
||||
<ion-icon *ngIf="!prefetch.loading" [name]="prefetch.icon" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-spinner *ngIf="prefetch.loading" slot="start" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ prefetch.statusTranslatable | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('delete')" detail="false"
|
||||
*ngIf="prefetch.status == 'downloaded' || prefetch.status == 'outdated'">
|
||||
<ion-icon name="fas-trash" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'addon.storagemanager.deletedata' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('hide')" *ngIf="!course.hidden" detail="false">
|
||||
<ion-icon name="fas-eye" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'core.courses.hidecourse' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('show')" *ngIf="course.hidden" detail="false">
|
||||
<ion-icon name="fas-eye-slash" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'core.courses.show' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('favourite')" *ngIf="!course.isfavourite" detail="false">
|
||||
<ion-icon name="fas-star" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'core.courses.addtofavourites' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button class="ion-text-wrap" (click)="action('unfavourite')" *ngIf="course.isfavourite" detail="false">
|
||||
<ion-icon name="far-star" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'core.courses.removefromfavourites' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
</div>
|
||||
|
||||
<div #toolbar class="core-rte-toolbar" [class.toolbar-hidden]="toolbarHidden">
|
||||
<button *ngIf="toolbarArrows" class="toolbar-arrow" [class.toolbar-arrow-hidden]="toolbarPrevHidden" (click)="toolbarPrev($event)"
|
||||
<button *ngIf="toolbarArrows" class="toolbar-arrow" [attr.disabled]="toolbarPrevHidden ? 'true' : null" (click)="toolbarPrev($event)"
|
||||
(keyup)="toolbarPrev($event)" (mousedown)="downAction($event)" (keydown)="downAction($event)"
|
||||
[attr.aria-label]="'core.previous' | translate">
|
||||
[attr.aria-label]="'core.previous' | translate" [tabindex]="toolbarPrevHidden ? -1 : 0">
|
||||
<ion-icon name="fas-chevron-left" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
<ion-slides [options]="slidesOpts" [dir]="direction" (ionSlideDidChange)="updateToolbarArrows()">
|
||||
|
@ -25,69 +25,70 @@
|
|||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.strong" [title]="'core.editor.bold' | translate"
|
||||
(click)="buttonAction($event, 'bold', 'strong')" (keyup)="buttonAction($event, 'bold', 'strong')"
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-bold" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.em" [title]="'core.editor.italic' | translate"
|
||||
(click)="buttonAction($event, 'italic', 'em')" (keyup)="buttonAction($event, 'italic', 'em')"
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-italic" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.u" [title]="'core.editor.underline' | translate"
|
||||
(click)="buttonAction($event, 'underline', 'u')" (keyup)="buttonAction($event, 'underline', 'u')"
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-underline" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.strike" [title]="'core.editor.strike' | translate"
|
||||
(click)="buttonAction($event, 'strikethrough', 'strike')" (keyup)="buttonAction($event, 'strikethrough', 'strike')"
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-strikethrough" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.p" [title]="'core.editor.p' | translate"
|
||||
(click)="buttonAction($event, 'p', 'block')" (keyup)="buttonAction($event, 'p', 'block')" (mousedown)="downAction($event)"
|
||||
(keydown)="downAction($event)">
|
||||
(keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-paragraph" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h3" [title]="'core.editor.h3' | translate"
|
||||
(click)="buttonAction($event, 'h3', 'block')" (keyup)="buttonAction($event, 'h3', 'block')" (mousedown)="downAction($event)"
|
||||
(keydown)="downAction($event)">
|
||||
(keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">3</span>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h4" [title]="'core.editor.h4' | translate"
|
||||
(click)="buttonAction($event, 'h4', 'block')" (keyup)="buttonAction($event, 'h4', 'block')" (mousedown)="downAction($event)"
|
||||
(keydown)="downAction($event)">
|
||||
(keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">4</span>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h5" [title]="'core.editor.h5' | translate"
|
||||
(click)="buttonAction($event, 'h5', 'block')" (keyup)="buttonAction($event, 'h5', 'block')" (mousedown)="downAction($event)"
|
||||
(keydown)="downAction($event)">
|
||||
(keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">5</span>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.ul" [title]="'core.editor.unorderedlist' | translate"
|
||||
(click)="buttonAction($event, 'insertUnorderedList')" (mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(click)="buttonAction($event, 'insertUnorderedList')" (mousedown)="downAction($event)" (keydown)="downAction($event)"
|
||||
tabindex="0">
|
||||
<ion-icon name="fas-list-ul" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.ol" [title]="'core.editor.orderedlist' | translate"
|
||||
(click)="buttonAction($event, 'insertOrderedList')" (keyup)="buttonAction($event, 'insertOrderedList')"
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-list-ol" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
|
@ -105,20 +106,20 @@
|
|||
</ion-slide>
|
||||
<ion-slide>
|
||||
<button [attr.aria-pressed]="!rteEnabled" [title]="'core.editor.toggle' | translate" (click)="toggleEditor($event)"
|
||||
(keyup)="toggleEditor($event)" (mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(keyup)="toggleEditor($event)" (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-code" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
<ion-slide *ngIf="isPhone">
|
||||
<button [title]="'core.editor.hidetoolbar' | translate" (click)="hideToolbar($event, true)" (keyup)="hideToolbar($event, true)"
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
|
||||
<ion-icon name="fas-times" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</ion-slide>
|
||||
</ion-slides>
|
||||
<button *ngIf="toolbarArrows" class="toolbar-arrow" [class.toolbar-arrow-hidden]="toolbarNextHidden"
|
||||
<button *ngIf="toolbarArrows" class="toolbar-arrow" [attr.disabled]="toolbarNextHidden ? 'true' : null"
|
||||
[attr.aria-label]="'core.next' | translate" (click)="toolbarNext($event)" (keyup)="toolbarNext($event)"
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)">
|
||||
(mousedown)="downAction($event)" (keydown)="downAction($event)" [tabindex]="toolbarNextHidden ? -1 : 0">
|
||||
<ion-icon name="fas-chevron-right" aria-hidden="true"></ion-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -149,8 +149,9 @@
|
|||
background-color: var(--toobar-background);
|
||||
}
|
||||
|
||||
&.toolbar-arrow-hidden {
|
||||
opacity: 0;
|
||||
&[disabled],
|
||||
&:disabled {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -391,7 +391,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
|
|||
|
||||
this.stopBubble(event);
|
||||
|
||||
const move = event.key == 'ArrowLeft' ? -1 : +1;
|
||||
const move = event.key === 'ArrowLeft' ? -1 : +1;
|
||||
const cursor = this.getCurrentCursorPosition(this.editorElement);
|
||||
|
||||
this.setCurrentCursorPosition(this.editorElement, cursor + move);
|
||||
|
@ -754,7 +754,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
|
|||
* @returns Wether space or enter have been pressed.
|
||||
*/
|
||||
protected isValidKeyboardKey(event: KeyboardEvent): boolean {
|
||||
return event.key == ' ' || event.key == 'Enter';
|
||||
return event.key === ' ' || event.key === 'Enter';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -353,7 +353,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
|
|||
* @param e Event.
|
||||
*/
|
||||
keyDown(e: KeyboardEvent): void {
|
||||
if (e.key == 'Escape') {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
@ -365,7 +365,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
|
|||
* @param e Event.
|
||||
*/
|
||||
keyUp(e: KeyboardEvent): void {
|
||||
if (e.key == 'Escape') {
|
||||
if (e.key === 'Escape') {
|
||||
this.cancel(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<core-user-avatar *ngIf="(alwaysShow || isMainScreen) && siteInfo" [user]="siteInfo" class="core-bar-button-image clickable"
|
||||
[linkProfile]="false" (ariaButtonClick)="openUserMenu($event)" [userTour]="userTour" role="button" tabindex="0"
|
||||
[linkProfile]="false" (ariaButtonClick)="openUserMenu($event)" [userTour]="userTour"
|
||||
[attr.aria-label]="'core.user.useraccount' | translate">
|
||||
</core-user-avatar>
|
||||
|
|
|
@ -514,22 +514,57 @@ export class CoreDom {
|
|||
*
|
||||
* @param element Element to listen to events.
|
||||
* @param callback Callback to call when clicked or the key is pressed.
|
||||
* @deprecated since 4.1.1: Use initializeClickableElementA11y instead.
|
||||
*/
|
||||
static onActivate(element: HTMLElement, callback: (event: MouseEvent | KeyboardEvent) => void): void {
|
||||
static onActivate(
|
||||
element: HTMLElement & {disabled?: boolean},
|
||||
callback: (event: MouseEvent | KeyboardEvent) => void,
|
||||
): void {
|
||||
this.initializeClickableElementA11y(element, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a clickable element a11y calling the click action when pressed enter or space
|
||||
* and adding tabindex and role if needed.
|
||||
*
|
||||
* @param element Element to listen to events.
|
||||
* @param callback Callback to call when clicked or the key is pressed.
|
||||
*/
|
||||
static initializeClickableElementA11y(
|
||||
element: HTMLElement & {disabled?: boolean},
|
||||
callback: (event: MouseEvent | KeyboardEvent) => void,
|
||||
): void {
|
||||
element.addEventListener('click', (event) => callback(event));
|
||||
|
||||
element.addEventListener('keydown', (event) => {
|
||||
if ((event.key == ' ' || event.key == 'Enter')) {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('keyup', (event) => {
|
||||
if ((event.key == ' ' || event.key == 'Enter')) {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
callback(event);
|
||||
}
|
||||
});
|
||||
|
||||
if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
|
||||
// Set tabindex if not previously set.
|
||||
if (element.getAttribute('tabindex') === null) {
|
||||
element.setAttribute('tabindex', element.disabled ? '-1' : '0');
|
||||
}
|
||||
|
||||
// Set role if not previously set.
|
||||
if (!element.getAttribute('role')) {
|
||||
element.setAttribute('role', 'button');
|
||||
}
|
||||
|
||||
element.classList.add('clickable');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
|
||||
@mixin core-focus-style() {
|
||||
box-shadow: inset 0 0 var(--a11y-focus-width) 1px var(--a11y-focus-color);
|
||||
border-radius: var(--border-radius);
|
||||
// Thicker option:
|
||||
// border: var(--a11y-focus-width) solid var(--a11y-focus-color);
|
||||
}
|
||||
|
|
|
@ -897,12 +897,16 @@ img[core-external-content]:not([src]) {
|
|||
}
|
||||
|
||||
ion-card {
|
||||
box-shadow: var(--box-shadow);
|
||||
margin: var(--ion-card-vertical-margin) var(--ion-card-horizontal-margin);
|
||||
border-width: var(--border-width);
|
||||
border-style: var(--border-style);
|
||||
border-color: var(--border-color);
|
||||
box-shadow: var(--box-shadow);
|
||||
border-radius: var(--border-radius);
|
||||
margin: var(--ion-card-vertical-margin) var(--ion-card-horizontal-margin);
|
||||
|
||||
&::part(native) {
|
||||
--border-width: 0;
|
||||
}
|
||||
|
||||
ion-item:only-child {
|
||||
--inner-border-width: 0px;
|
||||
|
@ -1514,19 +1518,24 @@ ion-item.item-input.item-multiple-inputs {
|
|||
}
|
||||
|
||||
// Focus highlight for accessibility.
|
||||
ion-item.item-input.ion-focused:not(:focus),
|
||||
.ion-focused,
|
||||
ion-item.ion-activatable.ion-focused:not(:focus),
|
||||
.ion-focused:not(.item-multiple-inputs):not(:focus),
|
||||
ion-input.has-focus,
|
||||
.ion-focused ion-toggle:focus-within,
|
||||
.ion-focused ion-select:focus-within,
|
||||
.ion-focused ion-checkbox:focus-within,
|
||||
.ion-focused ion-radio:focus-within {
|
||||
ion-card:focus {
|
||||
@include core-focus();
|
||||
}
|
||||
.ion-focused.item-multiple-inputs,
|
||||
.ion-focused.ion-activatable {
|
||||
ion-toggle:focus-within,
|
||||
ion-select:focus-within,
|
||||
ion-checkbox:focus-within,
|
||||
ion-radio:focus-within {
|
||||
@include core-focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Treat cases where there's a focusable element inside an item, like a button.
|
||||
ion-item.ion-focused:not(:focus),
|
||||
ion-item.item-input:not(.item-multiple-inputs):not(:focus),
|
||||
ion-item.item-has-focus:not(.item-multiple-inputs):not(:focus),
|
||||
ion-item.item-input ion-input.has-focus {
|
||||
position: relative;
|
||||
&::after {
|
||||
|
@ -1576,13 +1585,17 @@ ion-item.item {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
textarea, button, select, input, a {
|
||||
textarea, button, select, input, a, .clickable {
|
||||
&:focus {
|
||||
@include core-focus-style();
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ion-focused:not(.item-multiple-inputs):not(:focus) .clickable:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
ion-loading:focus-visible,
|
||||
ion-alert:focus-visible,
|
||||
ion-popover:focus-visible,
|
||||
|
|
Loading…
Reference in New Issue