Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e27c285
refactor(cdk/a11y): add ID generator service
crisbeto Oct 31, 2024
3a7c9db
refactor(cdk/accordion): use ID generator
crisbeto Oct 31, 2024
e7b57ee
refactor(cdk/stepper): use ID generator
crisbeto Oct 31, 2024
17eeade
refactor(material/datepicker): use ID generator
crisbeto Oct 31, 2024
3332898
refactor(material/badge): use ID generator
crisbeto Oct 31, 2024
b15ee19
refactor(cdk/dialog): use ID generator
crisbeto Oct 31, 2024
7d13dd8
refactor(cdk/drag-drop): use ID generator
crisbeto Oct 31, 2024
eb15a29
refactor(cdk/listbox): use ID generator
crisbeto Oct 31, 2024
6a7fd7b
refactor(cdk/menu): use ID generator
crisbeto Oct 31, 2024
7a8f780
refactor(cdk/overlay): use ID generator
crisbeto Oct 31, 2024
600dec5
refactor(cdk-experimental/column-resize): use ID generator
crisbeto Oct 31, 2024
410cc6b
refactor(cdk-experimental/combobox): use ID generator
crisbeto Oct 31, 2024
662850c
refactor(cdk-experimental/table-scroll-container): use ID generator
crisbeto Oct 31, 2024
e4ce1a0
refactor(material/autocomplete): use ID generator
crisbeto Oct 31, 2024
ac0ea70
refactor(material/button-toggle): use ID generator
crisbeto Oct 31, 2024
d13ce95
refactor(material/checkbox): use ID generator
crisbeto Oct 31, 2024
62e6763
refactor(material/chips): use ID generator
crisbeto Oct 31, 2024
0200e8b
refactor(material/core): use ID generator
crisbeto Oct 31, 2024
6d3c936
refactor(material/dialog): use ID generator
crisbeto Oct 31, 2024
bd9310b
refactor(material/expansion): use ID generator
crisbeto Oct 31, 2024
7af752a
refactor(material/form-field): use ID generator
crisbeto Oct 31, 2024
b710211
refactor(material/input): use ID generator
crisbeto Oct 31, 2024
93e36ff
refactor(material/menu): use ID generator
crisbeto Oct 31, 2024
c28615e
refactor(material/paginator): use ID generator
crisbeto Oct 31, 2024
bcc48e8
refactor(material/radio): use ID generator
crisbeto Oct 31, 2024
a059722
refactor(material/select): use ID generator
crisbeto Oct 31, 2024
024995b
refactor(material/slide-toggle): use ID generator
crisbeto Oct 31, 2024
c0befc6
refactor(material/snack-bar): use ID generator
crisbeto Oct 31, 2024
a165453
refactor(material/tabs): use ID generator
crisbeto Oct 31, 2024
74ea762
refactor(material/timepicker): use ID generator
crisbeto Oct 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/cdk-experimental/column-resize/column-resize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {AfterViewInit, Directive, ElementRef, NgZone, OnDestroy} from '@angular/core';
import {AfterViewInit, Directive, ElementRef, inject, NgZone, OnDestroy} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {fromEvent, merge, Subject} from 'rxjs';
import {filter, map, mapTo, pairwise, startWith, take, takeUntil} from 'rxjs/operators';

Expand All @@ -19,14 +20,13 @@ import {HeaderRowEventDispatcher} from './event-dispatcher';
const HOVER_OR_ACTIVE_CLASS = 'cdk-column-resize-hover-or-active';
const WITH_RESIZED_COLUMN_CLASS = 'cdk-column-resize-with-resized-column';

let nextId = 0;

/**
* Base class for ColumnResize directives which attach to mat-table elements to
* provide common events and services for column resizing.
*/
@Directive()
export abstract class ColumnResize implements AfterViewInit, OnDestroy {
private _idGenerator = inject(_IdGenerator);
protected readonly destroyed = new Subject<void>();

/* Publicly accessible interface for triggering and being notified of resizes. */
Expand All @@ -40,7 +40,7 @@ export abstract class ColumnResize implements AfterViewInit, OnDestroy {
protected abstract readonly notifier: ColumnResizeNotifierSource;

/** Unique ID for this table instance. */
protected readonly selectorId = `${++nextId}`;
protected readonly selectorId = this._idGenerator.getId('cdk-column-resize-');

/** The id attribute of the table, if specified. */
id?: string;
Expand All @@ -60,7 +60,7 @@ export abstract class ColumnResize implements AfterViewInit, OnDestroy {

/** Gets the unique CSS class name for this table instance. */
getUniqueCssClass() {
return `cdk-column-resize-${this.selectorId}`;
return this.selectorId;
}

/** Called when a column in the table is resized. Applies a css class to the table element. */
Expand Down
5 changes: 2 additions & 3 deletions src/cdk-experimental/combobox/combobox-popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
*/

import {Directive, ElementRef, Input, OnInit, inject} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {AriaHasPopupValue, CDK_COMBOBOX, CdkCombobox} from './combobox';

let nextId = 0;

@Directive({
selector: '[cdkComboboxPopup]',
exportAs: 'cdkComboboxPopup',
Expand Down Expand Up @@ -44,7 +43,7 @@ export class CdkComboboxPopup<T = unknown> implements OnInit {
}
private _firstFocusElement: HTMLElement;

@Input() id = `cdk-combobox-popup-${nextId++}`;
@Input() id: string = inject(_IdGenerator).getId('cdk-combobox-popup-');

ngOnInit() {
this.registerWithPanel();
Expand Down
1 change: 1 addition & 0 deletions src/cdk-experimental/table-scroll-container/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ng_module(
exclude = ["**/*.spec.ts"],
),
deps = [
"//src/cdk/a11y",
"//src/cdk/bidi",
"//src/cdk/platform",
"//src/cdk/table",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import {CSP_NONCE, Directive, ElementRef, OnDestroy, OnInit, inject} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {DOCUMENT} from '@angular/common';
import {Directionality} from '@angular/cdk/bidi';
import {_getShadowRoot} from '@angular/cdk/platform';
Expand All @@ -17,8 +18,6 @@ import {
StickyUpdate,
} from '@angular/cdk/table';

let nextId = 0;

/**
* Applies styles to the host element that make its scrollbars match up with
* the non-sticky scrollable portions of the CdkTable contained within.
Expand All @@ -43,7 +42,7 @@ export class CdkTableScrollContainer implements StickyPositioningListener, OnDes
private readonly _directionality = inject(Directionality, {optional: true});
private readonly _nonce = inject(CSP_NONCE, {optional: true});

private readonly _uniqueClassName = `cdk-table-scroll-container-${++nextId}`;
private readonly _uniqueClassName = inject(_IdGenerator).getId('cdk-table-scroll-container-');
private _styleRoot!: Node;
private _styleElement?: HTMLStyleElement;

Expand Down
40 changes: 40 additions & 0 deletions src/cdk/a11y/id-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @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 {APP_ID, inject, Injectable} from '@angular/core';

/**
* Keeps track of the ID count per prefix. This helps us make the IDs a bit more deterministic
* like they were before the service was introduced. Note that ideally we wouldn't have to do
* this, but there are some internal tests that rely on the IDs.
*/
const counters: Record<string, number> = {};

/** Service that generates unique IDs for DOM nodes. */
@Injectable({providedIn: 'root'})
export class _IdGenerator {
private _appId = inject(APP_ID);

/**
* Generates a unique ID with a specific prefix.
* @param prefix Prefix to add to the ID.
*/
getId(prefix: string): string {
// Omit the app ID if it's the default `ng`. Since the vast majority of pages have one
// Angular app on them, we can reduce the amount of breakages by not adding it.
if (this._appId !== 'ng') {
prefix += this._appId;
}

if (!counters.hasOwnProperty(prefix)) {
counters[prefix] = 0;
}

return `${prefix}${counters[prefix]++}`;
}
}
1 change: 1 addition & 0 deletions src/cdk/a11y/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export {
HighContrastModeDetector,
HighContrastMode,
} from './high-contrast-mode/high-contrast-mode-detector';
export {_IdGenerator} from './id-generator';
1 change: 1 addition & 0 deletions src/cdk/accordion/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ng_module(
exclude = ["**/*.spec.ts"],
),
deps = [
"//src/cdk/a11y",
"//src/cdk/collections",
"@npm//@angular/core",
"@npm//rxjs",
Expand Down
6 changes: 2 additions & 4 deletions src/cdk/accordion/accordion-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ import {
inject,
OnInit,
} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
import {CDK_ACCORDION, CdkAccordion} from './accordion';
import {Subscription} from 'rxjs';

/** Used to generate unique ID for each accordion item. */
let nextId = 0;

/**
* A basic directive expected to be extended and decorated as a component. Sets up all
* events and attributes needed to be managed by a CdkAccordion parent.
Expand Down Expand Up @@ -59,7 +57,7 @@ export class CdkAccordionItem implements OnInit, OnDestroy {
@Output() readonly expandedChange: EventEmitter<boolean> = new EventEmitter<boolean>();

/** The unique AccordionItem id. */
readonly id: string = `cdk-accordion-child-${nextId++}`;
readonly id: string = inject(_IdGenerator).getId('cdk-accordion-child-');

/** Whether the AccordionItem is expanded. */
@Input({transform: booleanAttribute})
Expand Down
7 changes: 3 additions & 4 deletions src/cdk/accordion/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ import {
OnDestroy,
SimpleChanges,
booleanAttribute,
inject,
} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {Subject} from 'rxjs';

/** Used to generate unique ID for each accordion. */
let nextId = 0;

/**
* Injection token that can be used to reference instances of `CdkAccordion`. It serves
* as alternative token to the actual `CdkAccordion` class which could cause unnecessary
Expand All @@ -43,7 +42,7 @@ export class CdkAccordion implements OnDestroy, OnChanges {
readonly _openCloseAllActions: Subject<boolean> = new Subject<boolean>();

/** A readonly id value to use for unique selection coordination. */
readonly id: string = `cdk-accordion-${nextId++}`;
readonly id: string = inject(_IdGenerator).getId('cdk-accordion-');

/** Whether the accordion should allow multiple expanded accordion items simultaneously. */
@Input({transform: booleanAttribute}) multi: boolean = false;
Expand Down
7 changes: 3 additions & 4 deletions src/cdk/dialog/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {of as observableOf, Observable, Subject, defer} from 'rxjs';
import {DialogRef} from './dialog-ref';
import {DialogConfig} from './dialog-config';
import {Directionality} from '@angular/cdk/bidi';
import {_IdGenerator} from '@angular/cdk/a11y';
import {
ComponentType,
Overlay,
Expand All @@ -33,16 +34,14 @@ import {startWith} from 'rxjs/operators';
import {DEFAULT_DIALOG_CONFIG, DIALOG_DATA, DIALOG_SCROLL_STRATEGY} from './dialog-injectors';
import {CdkDialogContainer} from './dialog-container';

/** Unique id for the created dialog. */
let uniqueId = 0;

@Injectable({providedIn: 'root'})
export class Dialog implements OnDestroy {
private _overlay = inject(Overlay);
private _injector = inject(Injector);
private _defaultOptions = inject<DialogConfig>(DEFAULT_DIALOG_CONFIG, {optional: true});
private _parentDialog = inject(Dialog, {optional: true, skipSelf: true});
private _overlayContainer = inject(OverlayContainer);
private _idGenerator = inject(_IdGenerator);

private _openDialogsAtThisLevel: DialogRef<any, any>[] = [];
private readonly _afterAllClosedAtThisLevel = new Subject<void>();
Expand Down Expand Up @@ -110,7 +109,7 @@ export class Dialog implements OnDestroy {
DialogRef<R, C>
>;
config = {...defaults, ...config};
config.id = config.id || `cdk-dialog-${uniqueId++}`;
config.id = config.id || this._idGenerator.getId('cdk-dialog-');

if (
config.id &&
Expand Down
6 changes: 2 additions & 4 deletions src/cdk/drag-drop/directives/drop-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
inject,
} from '@angular/core';
import {Directionality} from '@angular/cdk/bidi';
import {_IdGenerator} from '@angular/cdk/a11y';
import {ScrollDispatcher} from '@angular/cdk/scrolling';
import {CDK_DROP_LIST, CdkDrag} from './drag';
import {CdkDragDrop, CdkDragEnter, CdkDragExit, CdkDragSortEvent} from '../drag-events';
Expand All @@ -31,9 +32,6 @@ import {merge, Subject} from 'rxjs';
import {startWith, takeUntil} from 'rxjs/operators';
import {assertElementNode} from './assertions';

/** Counter used to generate unique ids for drop zones. */
let _uniqueIdCounter = 0;

/** Container that wraps a set of draggable items. */
@Directive({
selector: '[cdkDropList], cdk-drop-list',
Expand Down Expand Up @@ -91,7 +89,7 @@ export class CdkDropList<T = any> implements OnDestroy {
* Unique ID for the drop zone. Can be used as a reference
* in the `connectedTo` of another `CdkDropList`.
*/
@Input() id: string = `cdk-drop-list-${_uniqueIdCounter++}`;
@Input() id: string = inject(_IdGenerator).getId('cdk-drop-list-');

/** Locks the position of the draggable elements inside the container along the specified axis. */
@Input('cdkDropListLockAxis') lockAxis: DragAxis;
Expand Down
4 changes: 2 additions & 2 deletions src/cdk/listbox/listbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ describe('CdkOption and CdkListbox', () => {
expect(optionIds.size).toBe(options.length);
for (let i = 0; i < options.length; i++) {
expect(options[i].id).toBe(optionEls[i].id);
expect(options[i].id).toMatch(/cdk-option-\d+/);
expect(options[i].id).toMatch(/cdk-option-\w+\d+/);
}
expect(listbox.id).toEqual(listboxEl.id);
expect(listbox.id).toMatch(/cdk-listbox-\d+/);
expect(listbox.id).toMatch(/cdk-listbox-\w+\d+/);
});

it('should not overwrite user given ids', () => {
Expand Down
14 changes: 8 additions & 6 deletions src/cdk/listbox/listbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {ActiveDescendantKeyManager, Highlightable, ListKeyManagerOption} from '@angular/cdk/a11y';
import {
_IdGenerator,
ActiveDescendantKeyManager,
Highlightable,
ListKeyManagerOption,
} from '@angular/cdk/a11y';
import {Directionality} from '@angular/cdk/bidi';
import {coerceArray} from '@angular/cdk/coercion';
import {SelectionModel} from '@angular/cdk/collections';
Expand Down Expand Up @@ -42,9 +47,6 @@ import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {defer, fromEvent, merge, Observable, Subject} from 'rxjs';
import {filter, map, startWith, switchMap, takeUntil} from 'rxjs/operators';

/** The next id to use for creating unique DOM IDs. */
let nextId = 0;

/**
* An implementation of SelectionModel that internally always represents the selection as a
* multi-selection. This is necessary so that we can recover the full selection if the user
Expand Down Expand Up @@ -104,7 +106,7 @@ export class CdkOption<T = unknown> implements ListKeyManagerOption, Highlightab
this._id = value;
}
private _id: string;
private _generatedId = `cdk-option-${nextId++}`;
private _generatedId = inject(_IdGenerator).getId('cdk-option-');

/** The value of this option. */
@Input('cdkOption') value: T;
Expand Down Expand Up @@ -262,7 +264,7 @@ export class CdkListbox<T = unknown> implements AfterContentInit, OnDestroy, Con
this._id = value;
}
private _id: string;
private _generatedId = `cdk-listbox-${nextId++}`;
private _generatedId = inject(_IdGenerator).getId('cdk-listbox-');

/** The tabindex to use when the listbox is enabled. */
@Input('tabindex')
Expand Down
7 changes: 2 additions & 5 deletions src/cdk/menu/menu-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
import {_IdGenerator, FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
import {Directionality} from '@angular/cdk/bidi';
import {
AfterContentInit,
Expand All @@ -30,9 +30,6 @@ import {CdkMenuItem} from './menu-item';
import {MENU_STACK, MenuStack, MenuStackItem} from './menu-stack';
import {PointerFocusTracker} from './pointer-focus-tracker';

/** Counter used to create unique IDs for menus. */
let nextId = 0;

/**
* Abstract directive that implements shared logic common to all menus.
* This class can be extended to create custom menu types.
Expand Down Expand Up @@ -70,7 +67,7 @@ export abstract class CdkMenuBase
protected readonly dir = inject(Directionality, {optional: true});

/** The id of the menu's host element. */
@Input() id = `cdk-menu-${nextId++}`;
@Input() id: string = inject(_IdGenerator).getId('cdk-menu-');

/** All child MenuItem elements nested in this Menu. */
@ContentChildren(CdkMenuItem, {descendants: true})
Expand Down
8 changes: 3 additions & 5 deletions src/cdk/menu/menu-item-radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
import {Directive, inject, OnDestroy} from '@angular/core';
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
import {_IdGenerator} from '@angular/cdk/a11y';
import {CdkMenuItemSelectable} from './menu-item-selectable';
import {CdkMenuItem} from './menu-item';

/** Counter used to set a unique id and name for a selectable item */
let nextId = 0;

/**
* A directive providing behavior for the "menuitemradio" ARIA role, which behaves similarly to
* a conventional radio-button. Any sibling `CdkMenuItemRadio` instances within the same `CdkMenu`
Expand All @@ -36,7 +34,7 @@ export class CdkMenuItemRadio extends CdkMenuItemSelectable implements OnDestroy
private readonly _selectionDispatcher = inject(UniqueSelectionDispatcher);

/** An ID to identify this radio item to the `UniqueSelectionDispatcher`. */
private _id = `${nextId++}`;
private _id = inject(_IdGenerator).getId('cdk-menu-item-radio-');

/** Function to unregister the selection dispatcher */
private _removeDispatcherListener: () => void;
Expand Down
Loading
Loading