commit
a62cb0ca1e
|
@ -91,7 +91,8 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ng-container *ngFor="let recording of recordings">
|
<ng-container *ngFor="let recording of recordings">
|
||||||
<ion-item *ngIf="recording.url" button class="addon-mod_bbb-recording-title" [attr.aria-expanded]="recording.expanded"
|
<ion-item *ngIf="recording.url" button class="addon-mod_bbb-recording-title" [attr.aria-expanded]="recording.expanded"
|
||||||
(click)="toggle(recording)" [attr.aria-label]="(recording.expanded ? 'core.collapse' : 'core.expand') | translate">
|
(click)="toggle(recording)" [attr.aria-label]="(recording.expanded ? 'core.collapse' : 'core.expand') | translate"
|
||||||
|
detail="false">
|
||||||
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
|
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
|
||||||
[class.expandable-status-icon-expanded]="recording.expanded">
|
[class.expandable-status-icon-expanded]="recording.expanded">
|
||||||
</ion-icon>
|
</ion-icon>
|
||||||
|
|
|
@ -16,6 +16,8 @@ export class CoreAriaRoleTab<T = unknown> {
|
||||||
|
|
||||||
componentInstance: T;
|
componentInstance: T;
|
||||||
|
|
||||||
|
protected selectTabCandidate?: string;
|
||||||
|
|
||||||
constructor(componentInstance: T) {
|
constructor(componentInstance: T) {
|
||||||
this.componentInstance = componentInstance;
|
this.componentInstance = componentInstance;
|
||||||
}
|
}
|
||||||
|
@ -23,9 +25,10 @@ export class CoreAriaRoleTab<T = unknown> {
|
||||||
/**
|
/**
|
||||||
* A11y key functionality that prevents keyDown events.
|
* A11y key functionality that prevents keyDown events.
|
||||||
*
|
*
|
||||||
|
* @param tabFindIndex Tab findable index.
|
||||||
* @param e Event.
|
* @param e Event.
|
||||||
*/
|
*/
|
||||||
keyDown(e: KeyboardEvent): void {
|
keyDown(tabFindIndex: string, e: KeyboardEvent): void {
|
||||||
if (e.key == ' ' ||
|
if (e.key == ' ' ||
|
||||||
e.key == 'Enter' ||
|
e.key == 'Enter' ||
|
||||||
e.key == 'Home' ||
|
e.key == 'Home' ||
|
||||||
|
@ -35,6 +38,11 @@ export class CoreAriaRoleTab<T = unknown> {
|
||||||
) {
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key == ' ' || e.key == 'Enter') {
|
||||||
|
this.selectTabCandidate = tabFindIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,20 +56,25 @@ export class CoreAriaRoleTab<T = unknown> {
|
||||||
* End: When a tab has focus, moves focus to the last tab.
|
* End: When a tab has focus, moves focus to the last tab.
|
||||||
* https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-2/tabs.html
|
* https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-2/tabs.html
|
||||||
*
|
*
|
||||||
* @param tabFindIndex Tab finable index.
|
* @param tabFindIndex Tab findable index.
|
||||||
* @param e Event.
|
* @param e Event.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
keyUp(tabFindIndex: string, e: KeyboardEvent): void {
|
keyUp(tabFindIndex: string, e: KeyboardEvent): void {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
if (e.key == ' ' || e.key == 'Enter') {
|
if (e.key == ' ' || e.key == 'Enter') {
|
||||||
this.selectTab(tabFindIndex, e);
|
if (this.selectTabCandidate === tabFindIndex) {
|
||||||
|
this.selectTab(tabFindIndex, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectTabCandidate = undefined;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const tabs = this.getSelectableTabs();
|
const tabs = this.getSelectableTabs();
|
||||||
|
|
||||||
let index = tabs.findIndex((tab) => tabFindIndex == tab.findIndex);
|
let index = tabs.findIndex((tab) => tabFindIndex == tab.findIndex);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<ng-container *ngFor="let tab of tabs">
|
<ng-container *ngFor="let tab of tabs">
|
||||||
<ion-slide role="presentation" [id]="tab.id! + '-tab'" tabindex="-1" [class.selected]="selected == tab.id"
|
<ion-slide role="presentation" [id]="tab.id! + '-tab'" tabindex="-1" [class.selected]="selected == tab.id"
|
||||||
class="{{tab.class}}">
|
class="{{tab.class}}">
|
||||||
<ion-tab-button (ionTabButtonClick)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown($event)"
|
<ion-tab-button (ionTabButtonClick)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown(tab.id, $event)"
|
||||||
(keyup)="tabAction.keyUp(tab.id, $event)" [tab]="tab.page" [layout]="layout" role="tab"
|
(keyup)="tabAction.keyUp(tab.id, $event)" [tab]="tab.page" [layout]="layout" role="tab"
|
||||||
[attr.aria-controls]="tab.id" [attr.aria-selected]="selected == tab.id"
|
[attr.aria-controls]="tab.id" [attr.aria-selected]="selected == tab.id"
|
||||||
[tabindex]="selected == tab.id ? 0 : -1">
|
[tabindex]="selected == tab.id ? 0 : -1">
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<ng-container *ngFor="let tab of tabs">
|
<ng-container *ngFor="let tab of tabs">
|
||||||
<ion-slide *ngIf="tab.enabled" role="presentation" [id]="tab.id! + '-tab'" [class.selected]="selected == tab.id"
|
<ion-slide *ngIf="tab.enabled" role="presentation" [id]="tab.id! + '-tab'" [class.selected]="selected == tab.id"
|
||||||
class="{{tab.class}}">
|
class="{{tab.class}}">
|
||||||
<ion-tab-button (click)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown($event)"
|
<ion-tab-button (click)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown(tab.id, $event)"
|
||||||
(keyup)="tabAction.keyUp(tab.id, $event)" [layout]="layout" role="tab" [attr.aria-controls]="tab.id"
|
(keyup)="tabAction.keyUp(tab.id, $event)" [layout]="layout" role="tab" [attr.aria-controls]="tab.id"
|
||||||
[attr.aria-selected]="selected == tab.id" [tabindex]="selected == tab.id ? 0 : -1">
|
[attr.aria-selected]="selected == tab.id" [tabindex]="selected == tab.id ? 0 : -1">
|
||||||
<ion-icon *ngIf="tab.icon" [name]="tab.icon" aria-hidden="true"></ion-icon>
|
<ion-icon *ngIf="tab.icon" [name]="tab.icon" aria-hidden="true"></ion-icon>
|
||||||
|
|
|
@ -54,6 +54,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
protected viewportPromise?: CoreCancellablePromise<void>;
|
protected viewportPromise?: CoreCancellablePromise<void>;
|
||||||
protected loadingHeight = false;
|
protected loadingHeight = false;
|
||||||
protected pageDidEnterListener?: EventListener;
|
protected pageDidEnterListener?: EventListener;
|
||||||
|
protected keyUpListener?: EventListener;
|
||||||
protected page?: HTMLElement;
|
protected page?: HTMLElement;
|
||||||
|
|
||||||
constructor(el: ElementRef, protected ionContent: IonContent) {
|
constructor(el: ElementRef, protected ionContent: IonContent) {
|
||||||
|
@ -83,7 +84,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
this.calculateHeight();
|
this.calculateHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.listenScrollEvents();
|
this.listenEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,9 +121,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup scroll event listener.
|
* Setup event listeners.
|
||||||
*/
|
*/
|
||||||
protected async listenScrollEvents(): Promise<void> {
|
protected async listenEvents(): Promise<void> {
|
||||||
if (!this.content || this.content?.classList.contains('has-collapsible-footer')) {
|
if (!this.content || this.content?.classList.contains('has-collapsible-footer')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -185,6 +186,17 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
this.calculateHeight();
|
this.calculateHeight();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Handle scroll when navigating using tab key and the selected element is behind the footer.
|
||||||
|
this.content.addEventListener('keyup', this.keyUpListener = (ev: KeyboardEvent) => {
|
||||||
|
if (ev.key !== 'Tab' || !document.activeElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.activeElement.getBoundingClientRect().bottom > this.element.getBoundingClientRect().top) {
|
||||||
|
document.activeElement.scrollIntoView({ block: 'center' });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -259,6 +271,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
if (this.endContentScrollListener) {
|
if (this.endContentScrollListener) {
|
||||||
this.content.removeEventListener('ionScrollEnd', this.endContentScrollListener);
|
this.content.removeEventListener('ionScrollEnd', this.endContentScrollListener);
|
||||||
}
|
}
|
||||||
|
if (this.keyUpListener) {
|
||||||
|
this.content.removeEventListener('keyup', this.keyUpListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.page && this.pageDidEnterListener) {
|
if (this.page && this.pageDidEnterListener) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<core-user-menu-button *ngIf="loaded && tabsPlacement == 'side'" [alwaysShow]="true"></core-user-menu-button>
|
<core-user-menu-button *ngIf="loaded && tabsPlacement == 'side'" [alwaysShow]="true"></core-user-menu-button>
|
||||||
|
|
||||||
<ion-tab-button *ngFor="let tab of tabs" (keydown)="tabAction.keyDown($event)" (keyup)="tabAction.keyUp(tab.page, $event)"
|
<ion-tab-button *ngFor="let tab of tabs" (keydown)="tabAction.keyDown(tab.page, $event)" (keyup)="tabAction.keyUp(tab.page, $event)"
|
||||||
[hidden]="!loaded && tab.hide" [tab]="tab.page" [disabled]="tab.hide" layout="label-hide" class="{{tab.class}}"
|
[hidden]="!loaded && tab.hide" [tab]="tab.page" [disabled]="tab.hide" layout="label-hide" class="{{tab.class}}"
|
||||||
[selected]="tab.page === selectedTab" [tabindex]="selectedTab == tab.page ? 0 : -1" [attr.aria-controls]="tab.id">
|
[selected]="tab.page === selectedTab" [tabindex]="selectedTab == tab.page ? 0 : -1" [attr.aria-controls]="tab.id">
|
||||||
<ion-icon class="core-tab-icon" [name]="tab.icon" aria-hidden="true"></ion-icon>
|
<ion-icon class="core-tab-icon" [name]="tab.icon" aria-hidden="true"></ion-icon>
|
||||||
|
@ -17,8 +17,9 @@
|
||||||
</span>
|
</span>
|
||||||
</ion-tab-button>
|
</ion-tab-button>
|
||||||
|
|
||||||
<ion-tab-button (keydown)="tabAction.keyDown($event)" (keyup)="tabAction.keyUp(morePageName, $event)" [hidden]="!loaded"
|
<ion-tab-button (keydown)="tabAction.keyDown(morePageName, $event)" (keyup)="tabAction.keyUp(morePageName, $event)"
|
||||||
[tab]="morePageName" layout="label-hide" [tabindex]="selectedTab == morePageName ? 0 : -1" [attr.aria-controls]="morePageName">
|
[hidden]="!loaded" [tab]="morePageName" layout="label-hide" [tabindex]="selectedTab == morePageName ? 0 : -1"
|
||||||
|
[attr.aria-controls]="morePageName">
|
||||||
<ion-icon class="core-tab-icon" name="ellipsis-horizontal" aria-hidden="true"></ion-icon>
|
<ion-icon class="core-tab-icon" name="ellipsis-horizontal" aria-hidden="true"></ion-icon>
|
||||||
<ion-label aria-hidden="true">{{ 'core.more' | translate }}</ion-label>
|
<ion-label aria-hidden="true">{{ 'core.more' | translate }}</ion-label>
|
||||||
<span class="sr-only">{{ 'core.more' | translate }}</span>
|
<span class="sr-only">{{ 'core.more' | translate }}</span>
|
||||||
|
|
|
@ -362,4 +362,11 @@ class CoreMainMenuRoleTab extends CoreAriaRoleTab<CoreMainMenuPage> {
|
||||||
return this.componentInstance.tabsPlacement == 'bottom';
|
return this.componentInstance.tabsPlacement == 'bottom';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
selectTab(tabId: string): void {
|
||||||
|
this.componentInstance.mainTabs?.select(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue