/
githubmirror
/
components
Обзор
Документация
Войти
/
githubmirror
/
components
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/aria/tabs/tab.ts
124 строки
3 KB
Kristiyan Kostadinov
fix(multiple): prevent form submissions in aria directives (#33297)
21 май 2026, 14:21
Не верифицирован
21 май 2026, 14:21
11e2eb3
Код
Авторство
О чём код?
/** * @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 {_IdGenerator} from '@angular/cdk/a11y'; import { Directive, ElementRef, OnInit, OnDestroy, booleanAttribute, computed, inject, input, afterRenderEffect, } from '@angular/core'; import {TabPattern, HasElement, reportViolations} from '../private'; import {TAB_LIST} from './tab-tokens'; /** * A selectable tab in a TabList. * * The `ngTab` directive represents an individual tab control within an `ngTabList`. It * requires a `value` that uniquely identifies it and links it to a corresponding `ngTabPanel`. * * ```html * <li ngTab value="myTabId" [disabled]="isTabDisabled"> * My Tab Label * </li> * ``` * * @see [Tabs](guide/aria/tabs) */ @Directive({ selector: '[ngTab]', exportAs: 'ngTab', host: { 'role': 'tab', '[attr.data-active]': 'active()', '[attr.id]': '_pattern.id()', '[attr.tabindex]': '_pattern.tabIndex()', '[attr.aria-selected]': 'selected()', '[attr.aria-disabled]': '_pattern.disabled()', '[attr.aria-controls]': '_pattern.controls()', }, }) export class Tab implements HasElement, OnInit, OnDestroy { /** A reference to the host element. */ private readonly _elementRef = inject(ElementRef); /** A reference to the host element. */ readonly element = this._elementRef.nativeElement as HTMLElement; /** The parent TabList. */ private readonly _tabList = inject(TAB_LIST); /** A unique identifier for the widget. */ readonly id = input(inject(_IdGenerator).getId('ng-tab-', true)); /** The TabPanel UIPattern associated with the tab */ private readonly _tabpanelPattern = computed(() => { return this._tabList._tabsParent._panelMap().get(this.value()); }); /** Whether a tab is disabled. */ readonly disabled = input(false, {transform: booleanAttribute}); /** The remote tabpanel unique identifier. */ readonly value = input.required<string>(); /** Whether the tab is active. */ readonly active = computed(() => this._pattern.active()); /** Whether the tab is selected. */ readonly selected = computed(() => this._pattern.selected()); /** The Tab UIPattern. */ readonly _pattern: TabPattern = new TabPattern({ ...this, element: () => this.element, tabList: () => this._tabList._pattern, tabPanel: this._tabpanelPattern, }); /** Opens this tab panel. */ open() { this._pattern.open(); } constructor() { // Automatically prevent form submission. if (this.element.tagName === 'BUTTON' && !this.element.hasAttribute('type')) { this.element.setAttribute('type', 'button'); } if (typeof ngDevMode === 'undefined' || ngDevMode) { afterRenderEffect({ read: () => { const violations: string[] = []; if (this._tabList && this._tabList._tabsParent) { if (!this._tabList._tabsParent._panelMap().has(this.value())) { violations.push( `ngTab with value '${this.value()}' does not have a corresponding ngTabPanel.`, ); } } reportViolations(violations, this.element); }, }); } } ngOnInit() { this._tabList._collection.register(this); } ngOnDestroy() { this._tabList._collection.unregister(this); } }