MOBILE-4065 directive: Improve ariaButtonClick directive
parent
15cdc017e5
commit
7fac6895d9
|
@ -1,10 +1,14 @@
|
||||||
<img *ngIf="avatarUrl" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
|
<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)" [attr.aria-hidden]="!linkProfile"
|
onError="this.src='assets/img/user-avatar.png'" (ariaButtonClick)="gotoProfile($event)">
|
||||||
[attr.role]="linkProfile ? 'button' : null" [attr.tabindex]="linkProfile ? 0 : null" [class.clickable]="linkProfile">
|
|
||||||
|
|
||||||
<img *ngIf="!avatarUrl" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
|
<img *ngIf="avatarUrl && !linkProfile" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
|
||||||
(ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" [attr.role]="linkProfile ? 'button' : null"
|
onError="this.src='assets/img/user-avatar.png'" aria-hidden="true">
|
||||||
[attr.tabindex]="linkProfile ? 0 : null">
|
|
||||||
|
<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 *ngIf="checkOnline && isOnline()" class="contact-status online" role="status" [attr.aria-label]="'core.online' | translate">
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
width: var(--core-avatar-size);
|
width: var(--core-avatar-size);
|
||||||
height: var(--core-avatar-size);
|
height: var(--core-avatar-size);
|
||||||
|
|
||||||
.clickable {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
img {
|
img {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: var(--core-avatar-size);
|
width: var(--core-avatar-size);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { 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';
|
import { CoreDom } from '@singletons/dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,10 +21,11 @@ import { CoreDom } from '@singletons/dom';
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[ariaButtonClick]',
|
selector: '[ariaButtonClick]',
|
||||||
})
|
})
|
||||||
export class CoreAriaButtonClickDirective implements OnInit {
|
export class CoreAriaButtonClickDirective implements OnInit, OnChanges {
|
||||||
|
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
|
|
||||||
|
@Input() disabled = false;
|
||||||
@Output() ariaButtonClick = new EventEmitter();
|
@Output() ariaButtonClick = new EventEmitter();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -34,10 +35,27 @@ export class CoreAriaButtonClickDirective implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize actions.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
|
CoreDom.initializeClickableElementA11y(element, async (event) => {
|
||||||
element.setAttribute('tabindex', '0');
|
|
||||||
element.setAttribute('role', 'button');
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreDom.onActivate(element, async (event) => {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,7 @@ export class CoreLinkDirective implements OnInit {
|
||||||
* Function executed when the component is initialized.
|
* Function executed when the component is initialized.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this.element.tagName != 'BUTTON' && this.element.tagName != 'A') {
|
CoreDom.initializeClickableElementA11y(this.element, (event) => this.performAction(event));
|
||||||
this.element.setAttribute('tabindex', '0');
|
|
||||||
this.element.setAttribute('role', 'button');
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreDom.onActivate(this.element, (event) => this.performAction(event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<core-user-avatar *ngIf="(alwaysShow || isMainScreen) && siteInfo" [user]="siteInfo" class="core-bar-button-image clickable"
|
<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">
|
[attr.aria-label]="'core.user.useraccount' | translate">
|
||||||
</core-user-avatar>
|
</core-user-avatar>
|
||||||
|
|
|
@ -514,8 +514,26 @@ export class CoreDom {
|
||||||
*
|
*
|
||||||
* @param element Element to listen to events.
|
* @param element Element to listen to events.
|
||||||
* @param callback Callback to call when clicked or the key is pressed.
|
* @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('click', (event) => callback(event));
|
||||||
|
|
||||||
element.addEventListener('keydown', (event) => {
|
element.addEventListener('keydown', (event) => {
|
||||||
|
@ -526,10 +544,27 @@ export class CoreDom {
|
||||||
});
|
});
|
||||||
|
|
||||||
element.addEventListener('keyup', (event) => {
|
element.addEventListener('keyup', (event) => {
|
||||||
if ((event.key == ' ' || event.key == 'Enter')) {
|
if (event.key === ' ' || event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
callback(event);
|
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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue