/** * @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 {BooleanInput, coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion'; import {Platform} from '@angular/cdk/platform'; import { AfterViewInit, ContentChildren, Directive, ElementRef, inject, Input, NgZone, OnDestroy, QueryList, ANIMATION_MODULE_TYPE, Injector, } from '@angular/core'; import { _StructuralStylesLoader, MAT_RIPPLE_GLOBAL_OPTIONS, RippleConfig, RippleGlobalOptions, RippleRenderer, RippleTarget, } from '@angular/material/core'; import {_CdkPrivateStyleLoader} from '@angular/cdk/private'; import {Subscription, merge} from 'rxjs'; import { MatListItemLine, MatListItemTitle, MatListItemIcon, MatListItemAvatar, } from './list-item-sections'; import {MAT_LIST_CONFIG} from './tokens'; @Directive({ host: { '[attr.aria-disabled]': 'disabled', }, }) /** @docs-private */ export abstract class MatListBase { _isNonInteractive: boolean = true; /** Whether ripples for all list items is disabled. */ @Input() get disableRipple(): boolean { return this._disableRipple; } set disableRipple(value: BooleanInput) { this._disableRipple = coerceBooleanProperty(value); } private _disableRipple: boolean = false; /** * Whether the entire list is disabled. When disabled, the list itself and each of its list items * are disabled. */ @Input() get disabled(): boolean { return this._disabled; } set disabled(value: BooleanInput) { this._disabled = coerceBooleanProperty(value); } private _disabled = false; protected _defaultOptions = inject(MAT_LIST_CONFIG, {optional: true}); } @Directive({ host: { '[class.mdc-list-item--disabled]': 'disabled', '[attr.aria-disabled]': 'disabled', '[attr.disabled]': '(_isButtonElement && disabled) || null', }, }) /** @docs-private */ export abstract class MatListItemBase implements AfterViewInit, OnDestroy, RippleTarget { _elementRef = inject>(ElementRef); protected _ngZone = inject(NgZone); private _listBase = inject(MatListBase, {optional: true}); private _platform = inject(Platform); /** Query list matching list-item line elements. */ abstract _lines: QueryList | undefined; /** Query list matching list-item title elements. */ abstract _titles: QueryList | undefined; /** * Element reference to the unscoped content in a list item. * * Unscoped content is user-projected text content in a list item that is * not part of an explicit line or title. */ abstract _unscopedContent: ElementRef | undefined; /** Host element for the list item. */ _hostElement: HTMLElement; /** indicate whether the host element is a button or not */ _isButtonElement: boolean; /** Whether animations are disabled. */ _noopAnimations: boolean; @ContentChildren(MatListItemAvatar, {descendants: false}) _avatars: QueryList; @ContentChildren(MatListItemIcon, {descendants: false}) _icons: QueryList; /** * The number of lines this list item should reserve space for. If not specified, * lines are inferred based on the projected content. * * Explicitly specifying the number of lines is useful if you want to acquire additional * space and enable the wrapping of text. The unscoped text content of a list item will * always be able to take up the remaining space of the item, unless it represents the title. * * A maximum of three lines is supported as per the Material Design specification. */ @Input() set lines(lines: number | string | null) { this._explicitLines = coerceNumberProperty(lines, null); this._updateItemLines(false); } _explicitLines: number | null = null; /** Whether ripples for list items are disabled. */ @Input() get disableRipple(): boolean { return ( this.disabled || this._disableRipple || this._noopAnimations || !!this._listBase?.disableRipple ); } set disableRipple(value: BooleanInput) { this._disableRipple = coerceBooleanProperty(value); } private _disableRipple: boolean = false; /** Whether the list-item is disabled. */ @Input() get disabled(): boolean { return this._disabled || !!this._listBase?.disabled; } set disabled(value: BooleanInput) { this._disabled = coerceBooleanProperty(value); } private _disabled = false; private _subscriptions = new Subscription(); private _rippleRenderer: RippleRenderer | null = null; /** Whether the list item has unscoped text content. */ _hasUnscopedTextContent: boolean = false; /** * Implemented as part of `RippleTarget`. * @docs-private */ rippleConfig: RippleConfig & RippleGlobalOptions; /** * Implemented as part of `RippleTarget`. * @docs-private */ get rippleDisabled(): boolean { return this.disableRipple || !!this.rippleConfig.disabled; } constructor(...args: unknown[]); constructor() { inject(_CdkPrivateStyleLoader).load(_StructuralStylesLoader); const globalRippleOptions = inject(MAT_RIPPLE_GLOBAL_OPTIONS, { optional: true, }); const animationMode = inject(ANIMATION_MODULE_TYPE, {optional: true}); this.rippleConfig = globalRippleOptions || {}; this._hostElement = this._elementRef.nativeElement; this._isButtonElement = this._hostElement.nodeName.toLowerCase() === 'button'; this._noopAnimations = animationMode === 'NoopAnimations'; if (this._listBase && !this._listBase._isNonInteractive) { this._initInteractiveListItem(); } // If no type attribute is specified for a host `