Skip to content

Commit 63c52aa

Browse files
committed
fixup! feat(cdk-experimental/ui-patterns): listbox ui pattern
1 parent edfc6f1 commit 63c52aa

File tree

17 files changed

+189
-94
lines changed

17 files changed

+189
-94
lines changed

src/cdk-experimental/listbox/listbox.ts

+15-15
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import {
1414
inject,
1515
input,
1616
model,
17+
OnDestroy,
1718
signal,
1819
} from '@angular/core';
19-
import {Option} from '../ui-patterns/listbox/option';
20-
import {ListboxInputs, ListboxPattern} from '../ui-patterns/listbox/listbox';
2120
import {Directionality} from '@angular/cdk/bidi';
21+
import {ListboxInputs, ListboxPattern, OptionPattern} from '@angular/cdk-experimental/ui-patterns';
2222
import {startWith, takeUntil} from 'rxjs/operators';
2323
import {Subject} from 'rxjs';
2424

@@ -40,7 +40,7 @@ import {Subject} from 'rxjs';
4040
// '(focusin)': '_handleFocusIn()',
4141
},
4242
})
43-
export class CdkListbox implements ListboxInputs {
43+
export class CdkListbox implements ListboxInputs, OnDestroy {
4444
/** Whether the list is vertically or horizontally oriented. */
4545
orientation = input<'vertical' | 'horizontal'>('vertical');
4646

@@ -69,31 +69,31 @@ export class CdkListbox implements ListboxInputs {
6969
activeIndex = model<number>(0);
7070

7171
/** The CdkOptions nested inside of the CdkListbox. */
72-
private cdkOptions = contentChildren(CdkOption, {descendants: true});
72+
private _cdkOptions = contentChildren(CdkOption, {descendants: true});
7373

7474
/** The Option UIPatterns of the child CdkOptions. */
75-
items = computed(() => this.cdkOptions().map(option => option.state));
75+
items = computed(() => this._cdkOptions().map(option => option.state));
7676

7777
/** The directionality (LTR / RTL) context for the application (or a subtree of it). */
78-
private dir = inject(Directionality);
78+
private _dir = inject(Directionality);
7979

8080
/** A signal wrapper for directionality. */
8181
directionality = signal<'rtl' | 'ltr'>('rtl');
8282

8383
/** Emits when the list has been destroyed. */
84-
private readonly destroyed = new Subject<void>();
84+
private readonly _destroyed = new Subject<void>();
8585

8686
/** The Listbox UIPattern. */
8787
state: ListboxPattern = new ListboxPattern(this);
8888

8989
constructor() {
90-
this.dir.change
91-
.pipe(startWith(this.dir.value), takeUntil(this.destroyed))
90+
this._dir.change
91+
.pipe(startWith(this._dir.value), takeUntil(this._destroyed))
9292
.subscribe(value => this.directionality.set(value));
9393
}
9494

9595
ngOnDestroy() {
96-
this.destroyed.complete();
96+
this._destroyed.complete();
9797
}
9898
}
9999

@@ -122,16 +122,16 @@ export class CdkOption {
122122
searchTerm = computed(() => this.label() ?? this.element().textContent);
123123

124124
/** A reference to the option element. */
125-
private elementRef = inject(ElementRef);
125+
private _elementRef = inject(ElementRef);
126126

127-
element = computed(() => this.elementRef.nativeElement);
127+
element = computed(() => this._elementRef.nativeElement);
128128

129129
/** The parent CdkListbox. */
130-
private cdkListbox = inject(CdkListbox);
130+
private _cdkListbox = inject(CdkListbox);
131131

132132
/** The parent Listbox UIPattern. */
133-
listbox = computed(() => this.cdkListbox.state);
133+
listbox = computed(() => this._cdkListbox.state);
134134

135135
/** The Option UIPattern. */
136-
state: Option = new Option(this);
136+
state = new OptionPattern(this);
137137
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
load("//tools:defaults.bzl", "ts_library")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_library(
6+
name = "ui-patterns",
7+
srcs = glob(
8+
["**/*.ts"],
9+
exclude = ["**/*.spec.ts"],
10+
),
11+
deps = [
12+
"//src/cdk-experimental/ui-patterns/behaviors/event-manager",
13+
"//src/cdk-experimental/ui-patterns/behaviors/list-focus",
14+
"//src/cdk-experimental/ui-patterns/behaviors/list-navigation",
15+
"//src/cdk-experimental/ui-patterns/behaviors/list-selection",
16+
"//src/cdk-experimental/ui-patterns/behaviors/list-typeahead",
17+
"@npm//@angular/core",
18+
],
19+
)

src/cdk-experimental/ui-patterns/behaviors/event-manager/event-manager.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
19
/**
210
* An event that supports modifier keys.
311
*/
@@ -38,7 +46,7 @@ export enum ModifierKey {
3846
* Abstract base class for all event managers.
3947
*/
4048
export abstract class EventManager<T extends Event> {
41-
private submanagers: EventManager<T>[] = [];
49+
private _submanagers: EventManager<T>[] = [];
4250

4351
protected configs: EventHandlerConfig<T>[] = [];
4452
protected beforeFns: ((event: T) => void)[] = [];
@@ -62,7 +70,7 @@ export abstract class EventManager<T extends Event> {
6270
*/
6371
static compose<T extends Event>(...managers: EventManager<T>[]) {
6472
const composedManager = new GenericEventManager<T>();
65-
composedManager.submanagers = managers;
73+
composedManager._submanagers = managers;
6674
return composedManager;
6775
}
6876

@@ -81,7 +89,7 @@ export abstract class EventManager<T extends Event> {
8189
for (const fn of this.beforeFns) {
8290
fn(event);
8391
}
84-
for (const submanager of this.submanagers) {
92+
for (const submanager of this._submanagers) {
8593
await submanager.handle(event);
8694
}
8795
for (const config of this.getConfigs(event)) {
@@ -130,7 +138,7 @@ export abstract class EventManager<T extends Event> {
130138
* Checks whether this event manager is confugred to handle the given event.
131139
*/
132140
protected isHandled(event: T): boolean {
133-
return this.getConfigs(event).length > 0 || this.submanagers.some(sm => sm.isHandled(event));
141+
return this.getConfigs(event).length > 0 || this._submanagers.some(sm => sm.isHandled(event));
134142
}
135143
}
136144

src/cdk-experimental/ui-patterns/behaviors/event-manager/keyboard-event-manager.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
19
import {Signal} from '@angular/core';
210
import {
311
EventHandlerConfig,
@@ -75,12 +83,12 @@ export class KeyboardEventManager extends EventManager<KeyboardEvent> {
7583
}
7684

7785
getConfigs(event: KeyboardEvent) {
78-
return this.configs.filter(config => this.checkKey(config, event));
86+
return this.configs.filter(config => this._checkKey(config, event));
7987
}
8088

8189
// TODO: Make modifiers accept a signal as well.
8290

83-
private checkKey(config: KeyboardEventHandlerConfig, event: KeyboardEvent) {
91+
private _checkKey(config: KeyboardEventHandlerConfig, event: KeyboardEvent) {
8492
if (config.key instanceof RegExp) {
8593
return config.key.test(event.key);
8694
}

src/cdk-experimental/ui-patterns/behaviors/event-manager/mouse-event-manager.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
19
import {
210
EventHandlerConfig,
311
EventHandlerOptions,

src/cdk-experimental/ui-patterns/behaviors/list-focus/list-focus.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
*/
88

99
import {computed, Signal} from '@angular/core';
10-
import {ListNavigation, ListNavigationItem} from '../list-navigation/list-navigation';
11-
import {ListFocusController} from './controller';
10+
import {ListNavigation, ListNavigationItem} from '@angular/cdk-experimental/ui-patterns';
11+
import type {ListFocusController} from './controller';
1212

1313
/** The required properties for focus items. */
1414
export interface ListFocusItem extends ListNavigationItem {
@@ -30,7 +30,7 @@ export class ListFocus<T extends ListFocusItem> {
3030
/** The navigation controller of the parent list. */
3131
navigation: ListNavigation<ListFocusItem>;
3232

33-
private get controller(): Promise<ListFocusController<T>> {
33+
get controller(): Promise<ListFocusController<T>> {
3434
if (this._controller === null) {
3535
return this.loadController();
3636
}
@@ -44,8 +44,8 @@ export class ListFocus<T extends ListFocusItem> {
4444

4545
/** Loads the controller for list focus. */
4646
async loadController(): Promise<ListFocusController<T>> {
47-
return import('./controller').then(({ListFocusController: ListFocusController}) => {
48-
this._controller = new ListFocusController(this);
47+
return import('./controller').then(m => {
48+
this._controller = new m.ListFocusController(this);
4949
return this._controller;
5050
});
5151
}

src/cdk-experimental/ui-patterns/behaviors/list-navigation/list-navigation.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class ListNavigation<T extends ListNavigationItem> {
4141
/** The last index that was active. */
4242
prevActiveIndex = signal(0);
4343

44-
private get controller(): Promise<ListNavigationController<T>> {
44+
get controller(): Promise<ListNavigationController<T>> {
4545
if (this._controller === null) {
4646
return this.loadController();
4747
}
@@ -55,8 +55,8 @@ export class ListNavigation<T extends ListNavigationItem> {
5555

5656
/** Loads the controller for list navigation. */
5757
async loadController(): Promise<ListNavigationController<T>> {
58-
return import('./controller').then(({ListNavigationController: ListNavigationController}) => {
59-
this._controller = new ListNavigationController(this);
58+
return import('./controller').then(m => {
59+
this._controller = new m.ListNavigationController(this);
6060
return this._controller;
6161
});
6262
}

src/cdk-experimental/ui-patterns/behaviors/list-selection/controller.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {ListSelectionItem, ListSelection} from './list-selection';
1212
export class ListSelectionController<T extends ListSelectionItem> {
1313
constructor(readonly state: ListSelection<T>) {
1414
if (this.state.inputs.selectedIds()) {
15-
this.anchor();
15+
this._anchor();
1616
}
1717
}
1818

@@ -29,7 +29,7 @@ export class ListSelectionController<T extends ListSelectionItem> {
2929
}
3030

3131
// TODO: Need to discuss when to drop this.
32-
this.anchor();
32+
this._anchor();
3333
this.state.inputs.selectedIds.update(ids => ids.concat(item.id()));
3434
}
3535

@@ -64,7 +64,7 @@ export class ListSelectionController<T extends ListSelectionItem> {
6464
this.select(item);
6565
}
6666

67-
this.anchor();
67+
this._anchor();
6868
}
6969

7070
/** Deselects all items in the list. */
@@ -77,16 +77,16 @@ export class ListSelectionController<T extends ListSelectionItem> {
7777
/** Selects the items in the list starting at the last selected item. */
7878
selectFromAnchor() {
7979
const anchorIndex = this.state.inputs.items().findIndex(i => this.state.anchorId() === i.id());
80-
this.selectFromIndex(anchorIndex);
80+
this._selectFromIndex(anchorIndex);
8181
}
8282

8383
/** Selects the items in the list starting at the last active item. */
8484
selectFromActive() {
85-
this.selectFromIndex(this.state.inputs.navigation.prevActiveIndex());
85+
this._selectFromIndex(this.state.inputs.navigation.prevActiveIndex());
8686
}
8787

8888
/** Selects the items in the list starting at the given index. */
89-
private selectFromIndex(index: number) {
89+
private _selectFromIndex(index: number) {
9090
if (index === -1) {
9191
return;
9292
}
@@ -106,7 +106,7 @@ export class ListSelectionController<T extends ListSelectionItem> {
106106
}
107107

108108
/** Sets the anchor to the current active index. */
109-
private anchor() {
109+
private _anchor() {
110110
const item = this.state.inputs.items()[this.state.inputs.navigation.inputs.activeIndex()];
111111
this.state.anchorId.set(item.id());
112112
}

src/cdk-experimental/ui-patterns/behaviors/list-selection/list-selection.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {signal, Signal, WritableSignal} from '@angular/core';
1010
import {ListSelectionController} from './controller';
11-
import {ListNavigation, ListNavigationItem} from '../list-navigation/list-navigation';
11+
import {ListNavigation, ListNavigationItem} from '@angular/cdk-experimental/ui-patterns';
1212

1313
/** The required properties for selection items. */
1414
export interface ListSelectionItem extends ListNavigationItem {
@@ -42,7 +42,7 @@ export class ListSelection<T extends ListSelectionItem> {
4242
/** The navigation controller of the parent list. */
4343
navigation: ListNavigation<T>;
4444

45-
private get controller(): Promise<ListSelectionController<T>> {
45+
get controller(): Promise<ListSelectionController<T>> {
4646
if (this._controller === null) {
4747
return this.loadController();
4848
}
@@ -56,8 +56,8 @@ export class ListSelection<T extends ListSelectionItem> {
5656

5757
/** Loads the controller for list selection. */
5858
async loadController(): Promise<ListSelectionController<T>> {
59-
return import('./controller').then(({ListSelectionController: ListSelectionController}) => {
60-
this._controller = new ListSelectionController(this);
59+
return import('./controller').then(m => {
60+
this._controller = new m.ListSelectionController(this);
6161
return this._controller;
6262
});
6363
}

src/cdk-experimental/ui-patterns/behaviors/list-typeahead/controller.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class ListTypeaheadController<T extends ListTypeaheadItem> {
3434

3535
clearTimeout(this.timeout);
3636
this.query.update(q => q + char.toLowerCase());
37-
const item = await this.getItem();
37+
const item = await this._getItem();
3838

3939
if (item) {
4040
await this.state.navigation.goto(item);
@@ -50,7 +50,7 @@ export class ListTypeaheadController<T extends ListTypeaheadItem> {
5050
* Returns the first item whose search term matches the
5151
* current query starting from the the current anchor index.
5252
*/
53-
private async getItem() {
53+
private async _getItem() {
5454
let items = this.state.navigation.inputs.items();
5555
const after = items.slice(this.anchorIndex()! + 1);
5656
const before = items.slice(0, this.anchorIndex()!);

src/cdk-experimental/ui-patterns/behaviors/list-typeahead/list-typeahead.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {Signal} from '@angular/core';
1010
import type {ListTypeaheadController} from './controller';
11-
import {ListNavigationItem, ListNavigation} from '../list-navigation/list-navigation';
11+
import {ListNavigationItem, ListNavigation} from '@angular/cdk-experimental/ui-patterns';
1212

1313
/** The required properties for typeahead items. */
1414
export interface ListTypeaheadItem extends ListNavigationItem {
@@ -27,7 +27,7 @@ export class ListTypeahead<T extends ListTypeaheadItem> {
2727
/** The navigation controller of the parent list. */
2828
navigation: ListNavigation<T>;
2929

30-
private get controller(): Promise<ListTypeaheadController<T>> {
30+
get controller(): Promise<ListTypeaheadController<T>> {
3131
if (this._controller === null) {
3232
return this.loadController();
3333
}
@@ -41,8 +41,8 @@ export class ListTypeahead<T extends ListTypeaheadItem> {
4141

4242
/** Loads the controller for list typeahead. */
4343
async loadController(): Promise<ListTypeaheadController<T>> {
44-
return import('./controller').then(({ListTypeaheadController: ListTypeaheadController}) => {
45-
this._controller = new ListTypeaheadController(this);
44+
return import('./controller').then(m => {
45+
this._controller = new m.ListTypeaheadController(this);
4646
return this._controller;
4747
});
4848
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
export * from './public-api';

0 commit comments

Comments
 (0)