Skip to content

Commit 5ce4198

Browse files
committed
wip
1 parent ef7fa64 commit 5ce4198

File tree

8 files changed

+601
-3
lines changed

8 files changed

+601
-3
lines changed
21 KB
Loading
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
# UI Patterns
2+
3+
## Conceptual Description
4+
5+
Accessibility patterns are established, reusable solutions for creating accessible web content and
6+
applications. Some common accessibility patterns include the Tree View, Switch, Listbox, and Grid.
7+
8+
Behaviors are TypeScript classes that encapsulate the common behaviors of Accessibility Patterns.
9+
Some common behaviors include navigation and selection.
10+
11+
UI Patterns are TypeScript classes that encapsulate Accessibility Patterns. UI Patterns are built
12+
by utilizing multiple Behavior classes to implement the complete functionality of an Accessibility
13+
Pattern.
14+
15+
## Authoring Guidelines
16+
17+
### License Header
18+
19+
All .ts files should begin with an @license comment.
20+
21+
```ts
22+
/**
23+
* @license
24+
* Copyright Google LLC All Rights Reserved.
25+
*
26+
* Use of this source code is governed by an MIT-style license that can be
27+
* found in the LICENSE file at https://angular.dev/license
28+
*/
29+
```
30+
31+
### Imports
32+
33+
- Imports should be at the top of the file and kept in alphabetical order.
34+
- The only allowed external APIs are `signal`, and `computed` from `@angular/core`.
35+
- All other dependencies must come from inside of the ui-patterns folder.
36+
37+
### Type Definition
38+
39+
Each UI Pattern defines a type for it's constructor arguments.
40+
41+
All inputs must be signals. Inputs must not use the `Signal` or `WritableSignal` types from
42+
`@angular/core`. Instead, they must use the `SignalLike` and `WritableSignalLike` types from
43+
`ui-patterns/behaviors/signal-like`.
44+
45+
```ts
46+
export interface ExampleInputs extends Behavior1Inputs, Behavior2Inputs {
47+
// Additional inputs.
48+
}
49+
```
50+
51+
### Class Definition
52+
53+
#### How to instantiate Behaviors
54+
55+
All Behaviors are defined as class variables and instantiated in the constructor of the UI Pattern.
56+
57+
#### Core Methods
58+
59+
Each UI Pattern implements the following core methods:
60+
61+
- `validate()`
62+
- `setDefaultState()`
63+
- `onKeydown()`
64+
- `onPointerdown()`
65+
66+
```ts
67+
// License...
68+
69+
// Imports...
70+
71+
// Type Definition...
72+
73+
export class ExamplePattern {
74+
/** Controls behavior1 for the accessiblity pattern. */
75+
behavior1: Behavior1;
76+
77+
/** Controls behavior2 for the accessiblity pattern. */
78+
behavior2: Behavior2;
79+
80+
constructor(inputs: ExampleInputs) {
81+
this.behavior1 = new Behavior1(inputs);
82+
this.behavior2 = new Behavior2(inputs);
83+
}
84+
85+
/** Checks that the internal state of the pattern is valid. */
86+
validate(): string[] {}
87+
88+
/** Sets the default initial state of the accessibility pattern. */
89+
setDefaultState(): void {}
90+
91+
/** Handles keydown events for the accessibility pattern. */
92+
onKeydown(event: KeyboardEvent) {}
93+
94+
/** Handles pointer events for the accessibility pattern. */
95+
onPointerdown(event: PointerEvent) {}
96+
}
97+
```
98+
99+
#### Event Handlers
100+
101+
Event handlers for UI Patterns are authored utilizing the EventManager system from
102+
`ui-patterns/behaviors/event-manager`. The event-manager system exposes two primary event manager
103+
classes: `KeyboardEventManager` and `PointerEventManager`.
104+
105+
```ts
106+
// License...
107+
108+
// Imports...
109+
110+
// Type Definition...
111+
112+
export class ExamplePattern {
113+
// Behaviors...
114+
115+
/** The keydown event manager for the accessibility pattern. */
116+
keydown = computed(() => {
117+
return new KeyboardEventManager()
118+
.on('ArrowUp', () => { /* Arrow up logic. */ })
119+
.on('ArrowDown', () => { /* Arrow down logic. */ });
120+
});
121+
122+
/** The pointerdown event manager for the listbox. */
123+
pointerdown = computed(() => {
124+
return new PointerEventManager().on(() => {/* Pointerdown logic. */});
125+
});
126+
127+
// Constructor...
128+
129+
// Core Methods...
130+
131+
/** Handles keydown events for the accessibility pattern. */
132+
onKeydown(event: KeyboardEvent) {
133+
this.keydown().handle(event);
134+
}
135+
136+
/** Handles pointer events for the accessibility pattern. */
137+
onPointerdown(event: PointerEvent) {
138+
this.pointerdown().handle(event);
139+
}
140+
}
141+
```
142+
143+
##### KeyboardEventManager Expanded
144+
145+
The `KeyboardEventManager` class can accept strings, signals, and regular expressions as keys.
146+
147+
```ts
148+
/** The key used to navigate to the next item in the list. */
149+
nextKey = computed(() => { .. });
150+
151+
/** The key used to navigate to the next item in the list. */
152+
prevKey = computed(() => { ... });
153+
154+
/** The keydown event manager for the accessibility pattern. */
155+
keydown = computed(() => {
156+
return new KeyboardEventManager()
157+
.on(prevKey, () => { /* Navigate prev logic. */ })
158+
.on(nextKey, () => { /* Navigate next logic. */ });
159+
});
160+
```
161+
162+
The `KeyboardEventManager` can also accept a `Modifier` argument for handling modifier keys.
163+
164+
```ts
165+
/** The keydown event manager for the accessibility pattern. */
166+
keydown = computed(() => {
167+
return new KeyboardEventManager()
168+
.on(Modifier.Shift, 'ArrowUp', () => {
169+
/* Arrow up while holding shift logic. */
170+
});
171+
});
172+
```
173+
174+
The `Modifier` argument can be an array of modifier keys for defining the same behavior with
175+
multiple different modifier keys.
176+
177+
```ts
178+
/** The keydown event manager for the accessibility pattern. */
179+
keydown = computed(() => {
180+
return new KeyboardEventManager()
181+
.on([Modifier.Ctrl, Modifier.Meta], 'ArrowUp', () => {
182+
/* Arrow up while holding ctrl OR meta logic. */
183+
});
184+
});
185+
```
186+
187+
Holding multiple `Modifier` keys when performing a keydown event can be represented by using the
188+
bitwise OR operator.
189+
190+
```ts
191+
/** The keydown event manager for the accessibility pattern. */
192+
keydown = computed(() => {
193+
return new KeyboardEventManager()
194+
.on(Modifier.Ctrl | Modifier.Shift, 'ArrowUp', () => {
195+
/* Arrow up while holding ctrl + shift logic. */
196+
});
197+
});
198+
```
199+
200+
##### PointerEventManager Expanded
201+
202+
The `PointerEventManager` can also accept a `Modifier` argument for handling modifier keys.
203+
204+
```ts
205+
/** The pointerdown event manager for the accessibility pattern. */
206+
pointerdown = computed(() => {
207+
return new PointerEventManager()
208+
.on(Modifier.Shift, (e) => {
209+
/* Pointerdown while holding shift logic. */
210+
});
211+
});
212+
```
213+
214+
The `Modifier` argument can be an array of modifier keys for defining the same behavior with
215+
multiple different modifier keys.
216+
217+
```ts
218+
/** The pointerdown event manager for the accessibility pattern. */
219+
pointerdown = computed(() => {
220+
return new PointerEventManager()
221+
.on([Modifier.Ctrl, Modifier.Meta], () => {
222+
/* Pointerdown while holding ctrl OR meta logic. */
223+
});
224+
});
225+
```
226+
227+
Holding multiple `Modifier` keys when performing a pointerdown event can be represented by using the
228+
bitwise OR operator.
229+
230+
```ts
231+
/** The pointerdown event manager for the accessibility pattern. */
232+
pointerdown = computed(() => {
233+
return new PointerEventManager()
234+
.on(Modifier.Ctrl | Modifier.Shift, () => {
235+
/* Pointerdown while holding ctrl + shift logic. */
236+
});
237+
});
238+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export {ModifierKey} from './event-manager';
2+
export {KeyboardEventManager} from './keyboard-event-manager';
3+
export {PointerEventManager} from './pointer-event-manager';
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
load("//tools:defaults.bzl", "ng_web_test_suite", "ts_project")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_project(
6+
name = "combobox",
7+
srcs = glob(
8+
["**/*.ts"],
9+
exclude = ["**/*.spec.ts"],
10+
),
11+
deps = [
12+
"//:node_modules/@angular/core",
13+
"//src/cdk-experimental/ui-patterns/behaviors/event-manager",
14+
"//src/cdk-experimental/ui-patterns/behaviors/list-focus",
15+
"//src/cdk-experimental/ui-patterns/behaviors/list-navigation",
16+
"//src/cdk-experimental/ui-patterns/behaviors/list-selection",
17+
"//src/cdk-experimental/ui-patterns/behaviors/signal-like",
18+
"//src/cdk-experimental/ui-patterns/listbox",
19+
],
20+
)
21+
22+
ts_project(
23+
name = "unit_test_sources",
24+
testonly = True,
25+
srcs = glob(["**/*.spec.ts"]),
26+
deps = [
27+
":combobox",
28+
"//:node_modules/@angular/core",
29+
"//src/cdk/keycodes",
30+
"//src/cdk/testing/private",
31+
"//src/cdk-experimental/ui-patterns/listbox",
32+
],
33+
)
34+
35+
ng_web_test_suite(
36+
name = "unit_tests",
37+
deps = [":unit_test_sources"],
38+
)

0 commit comments

Comments
 (0)