sass-references/angular-material/material/core/option/optgroup.ts

86 lines
3.5 KiB
TypeScript
Raw Normal View History

2024-12-06 10:42:08 +08:00
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {
Component,
ViewEncapsulation,
ChangeDetectionStrategy,
Input,
InjectionToken,
booleanAttribute,
inject,
} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {MatOptionParentComponent, MAT_OPTION_PARENT_COMPONENT} from './option-parent';
// Notes on the accessibility pattern used for `mat-optgroup`.
// The option group has two different "modes": regular and inert. The regular mode uses the
// recommended a11y pattern which has `role="group"` on the group element with `aria-labelledby`
// pointing to the label. This works for `mat-select`, but it seems to hit a bug for autocomplete
// under VoiceOver where the group doesn't get read out at all. The bug appears to be that if
// there's __any__ a11y-related attribute on the group (e.g. `role` or `aria-labelledby`),
// VoiceOver on Safari won't read it out.
// We've introduced the `inert` mode as a workaround. Under this mode, all a11y attributes are
// removed from the group, and we get the screen reader to read out the group label by mirroring it
// inside an invisible element in the option. This is sub-optimal, because the screen reader will
// repeat the group label on each navigation, whereas the default pattern only reads the group when
// the user enters a new group. The following alternate approaches were considered:
// 1. Reading out the group label using the `LiveAnnouncer` solves the problem, but we can't control
// when the text will be read out so sometimes it comes in too late or never if the user
// navigates quickly.
// 2. `<mat-option aria-describedby="groupLabel"` - This works on Safari, but VoiceOver in Chrome
// won't read out the description at all.
// 3. `<mat-option aria-labelledby="optionLabel groupLabel"` - This works on Chrome, but Safari
// doesn't read out the text at all. Furthermore, on
/**
* Injection token that can be used to reference instances of `MatOptgroup`. It serves as
* alternative token to the actual `MatOptgroup` class which could cause unnecessary
* retention of the class and its component metadata.
*/
export const MAT_OPTGROUP = new InjectionToken<MatOptgroup>('MatOptgroup');
/**
* Component that is used to group instances of `mat-option`.
*/
@Component({
selector: 'mat-optgroup',
exportAs: 'matOptgroup',
templateUrl: 'optgroup.html',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
styleUrl: 'optgroup.css',
host: {
'class': 'mat-mdc-optgroup',
'[attr.role]': '_inert ? null : "group"',
'[attr.aria-disabled]': '_inert ? null : disabled.toString()',
'[attr.aria-labelledby]': '_inert ? null : _labelId',
},
providers: [{provide: MAT_OPTGROUP, useExisting: MatOptgroup}],
})
export class MatOptgroup {
/** Label for the option group. */
@Input() label: string;
/** whether the option group is disabled. */
@Input({transform: booleanAttribute}) disabled: boolean = false;
/** Unique id for the underlying label. */
_labelId: string = inject(_IdGenerator).getId('mat-optgroup-label-');
/** Whether the group is in inert a11y mode. */
_inert: boolean;
constructor(...args: unknown[]);
constructor() {
const parent = inject<MatOptionParentComponent>(MAT_OPTION_PARENT_COMPONENT, {optional: true});
this._inert = parent?.inertGroups ?? false;
}
}