Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@twnd/ux / core / option / optgroup.ts
Size: Mime:
/**
 * @license
 * FOURBURNER CONFIDENTIAL
 * Unpublished Copyright (C) 2021 FourBurner Technologies, Inc. All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property of FOURBURNER TECHNOLOGIES,
 * INC. The intellectual and technical concepts contained herein are proprietary to FOURBURNER
 * TECHNOLOGIES, INC. and may be covered by U.S. and Foreign Patents, patents in process, and are
 * protected by trade secret or copyright law. Dissemination of this information or reproduction of
 * this material is strictly forbidden unless prior written permission is obtained from FOURBURNER
 * TECHNOLOGIES, INC. Access to the source code contained herein is hereby forbidden to anyone
 * except current FOURBURNER TECHNOLOGIES, INC. employees, managers or contractors who have executed
 * Confidentiality and Non-disclosure agreements explicitly covering such access.
 *
 * The copyright notice above does not evidence any actual or intended publication or disclosure of
 * this source code, which includes information that is confidential and/or proprietary, and is a
 * trade secret, of FOURBURNER TECHNOLOGIES, INC. ANY REPRODUCTION, MODIFICATION, DISTRIBUTION,
 * PUBLIC PERFORMANCE, OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT THE EXPRESS
 * WRITTEN CONSENT OF FOURBURNER TECHNOLOGIES, INC. IS STRICTLY PROHIBITED, AND IN VIOLATION OF
 * APPLICABLE LAWS AND INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR
 * RELATED INFORMATION DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS
 * CONTENTS, OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART.
 */

import {BooleanInput} from '@angular/cdk/coercion';
import {
  ChangeDetectionStrategy,
  Component,
  InjectionToken,
  Input,
  ViewEncapsulation,
  Directive,
  Inject,
  Optional,
} from '@angular/core';
import {CanDisable, mixinDisabled} from '../common/disabled';
import {TWNDOptionParentComponent, TWND_OPTION_PARENT_COMPONENT} from './option.parent';

// Notes on the accessibility pattern used for `twnd-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 `twnd-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. `<twnd-option aria-describedby="groupLabel"` - This works on Safari, but VoiceOver in Chrome
//    won't read out the description at all.
// 3. `<twnd-option aria-labelledby="optionLabel groupLabel"` - This works on Chrome, but Safari
//     doesn't read out the text at all. Furthermore, on

// Boilerplate for applying mixins to TWNDOptgroup.
/** @docs-private */
const _TWNDOptgroupMixinBase = mixinDisabled(class {});

// Counter for unique group ids.
let _uniqueOptgroupIdCounter = 0;

@Directive() export class _TWNDOptgroupBase extends _TWNDOptgroupMixinBase implements CanDisable
{
  /** Label for the option group. */
  @Input() label: string;

  /** Unique id for the underlying label. */
  _labelId: string = `twnd-optgroup-label-${_uniqueOptgroupIdCounter++}`;

  /** Whether the group is in inert a11y mode. */
  _inert: boolean;

  constructor(@Inject(TWND_OPTION_PARENT_COMPONENT) @Optional() parent?: TWNDOptionParentComponent)
  {
    super();
    this._inert = parent?.inertGroups ?? false;
  }

  static ngAcceptInputType_disabled: BooleanInput;
}

/**
 * Injection token that can be used to reference instances of `TWNDOptgroup`. It serves as
 * alternative token to the actual `TWNDOptgroup` class which could cause unnecessary
 * retention of the class and its component metadata.
 */
export const TWND_OPTGROUP = new InjectionToken<TWNDOptgroup>('TWNDOptgroup');

/**
 * Component that is used to group instances of `twnd-option`.
 */
@Component({
  selector : 'twnd-optgroup',
  exportAs : 'twndOptgroup',
  templateUrl : 'optgroup.html',
  encapsulation : ViewEncapsulation.None,
  changeDetection : ChangeDetectionStrategy.OnPush,
  inputs : [ 'disabled' ],
  host : {
          'class' : 'twnd-optgroup',
          '[attr.role]' : '_inert ? null : "group"',
          '[attr.aria-disabled]' : '_inert ? null : disabled.toString()',
          '[attr.aria-labelledby]' : '_inert ? null : _labelId',
          '[class.twnd-optgroup-disabled]' : 'disabled',
          },
  providers : [ {provide : TWND_OPTGROUP, useExisting : TWNDOptgroup}
    ],
})
export class TWNDOptgroup extends _TWNDOptgroupBase
{}