Skip to content

Commit 666d646

Browse files
iterianithePunderWoman
authored andcommitted
feat(core): Add event delegation library to queue up events and replay them when the application is ready (#55121)
This adds the JSAction library from the Wiz framework to core/primitives. PR Close #55121
1 parent 840c375 commit 666d646

31 files changed

+8069
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
load("//tools:defaults.bzl", "ts_library", "tsec_test")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_library(
6+
name = "event-dispatch",
7+
srcs = glob(
8+
[
9+
"**/*.ts",
10+
],
11+
),
12+
module_name = "@angular/core/primitives/event-dispatch",
13+
)
14+
15+
tsec_test(
16+
name = "tsec_test",
17+
target = "event-dispatch",
18+
tsconfig = "//packages:tsec_config",
19+
)
20+
21+
filegroup(
22+
name = "files_for_docgen",
23+
srcs = glob([
24+
"*.ts",
25+
"src/**/*.ts",
26+
]),
27+
visibility = ["//visibility:public"],
28+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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.io/license
7+
*/
8+
9+
10+
export {Dispatcher, registerDispatcher} from './src/dispatcher';
11+
export {EventContractContainer} from './src/event_contract_container';
12+
export {EventContract} from './src/eventcontract';
13+
export {bootstrapEventContract} from './src/register_events';
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.io/license
7+
*/
8+
9+
import * as eventLib from './event';
10+
import * as eventInfoLib from './event_info';
11+
import {EventType} from './event_type';
12+
13+
/**
14+
* Update `EventInfo` to be `eventType = 'click'` and sets `a11yClickKey` if it
15+
* is a a11y click.
16+
*/
17+
export function updateEventInfoForA11yClick(eventInfo: eventInfoLib.EventInfo) {
18+
if (!eventLib.isActionKeyEvent(eventInfoLib.getEvent(eventInfo))) {
19+
return;
20+
}
21+
eventInfoLib.setA11yClickKey(eventInfo, true);
22+
// A 'click' triggered by a DOM keypress should be mapped to the 'click'
23+
// jsaction.
24+
eventInfoLib.setEventType(eventInfo, EventType.CLICK);
25+
}
26+
27+
/**
28+
* Call `preventDefault` on an a11y click if it is space key or to prevent the
29+
* browser's default action for native HTML controls.
30+
*/
31+
export function preventDefaultForA11yClick(eventInfo: eventInfoLib.EventInfo) {
32+
if (!eventInfoLib.getA11yClickKey(eventInfo) ||
33+
(!eventLib.isSpaceKeyEvent(eventInfoLib.getEvent(eventInfo)) &&
34+
!eventLib.shouldCallPreventDefaultOnNativeHtmlControl(
35+
eventInfoLib.getEvent(eventInfo),
36+
))) {
37+
return;
38+
}
39+
eventLib.preventDefault(eventInfoLib.getEvent(eventInfo));
40+
}
41+
42+
/**
43+
* Sets the `action` to `clickonly` for a click event that is not an a11y click
44+
* and if there is not already a click action.
45+
*/
46+
export function populateClickOnlyAction(
47+
eventInfo: eventInfoLib.EventInfo,
48+
actionMap: {[key: string]: string},
49+
) {
50+
if (eventInfoLib.getEventType(eventInfo) === EventType.CLICK &&
51+
// No a11y clicks should map to 'clickonly'.
52+
!eventInfoLib.getA11yClickKey(eventInfo) && !actionMap[EventType.CLICK] &&
53+
actionMap[EventType.CLICKONLY]) {
54+
// A 'click' triggered by a DOM click should be mapped to the 'click'
55+
// jsaction, if available, or else fallback to the 'clickonly' jsaction.
56+
// If 'click' and 'clickonly' jsactions are used together, 'click' will
57+
// prevail.
58+
eventInfoLib.setEventType(eventInfo, EventType.CLICKONLY);
59+
eventInfoLib.setAction(eventInfo, actionMap[EventType.CLICKONLY]);
60+
}
61+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.io/license
7+
*/
8+
9+
/**
10+
* Defines special EventInfo and Event properties used when
11+
* A11Y_SUPPORT_IN_DISPATCHER is enabled.
12+
*/
13+
export enum Attribute {
14+
/**
15+
* An event-type set when the event contract detects a KEYDOWN event but
16+
* doesn't know if the key press can be treated like a click. The dispatcher
17+
* will use this event-type to parse the keypress and handle it accordingly.
18+
*/
19+
MAYBE_CLICK_EVENT_TYPE = 'maybe_click',
20+
21+
/**
22+
* A property added to a dispatched event that had the MAYBE_CLICK_EVENTTYPE
23+
* event-type but could not be used as a click. The dispatcher sets this
24+
* property for non-global dispatches before it retriggers the event and it
25+
* signifies that the event contract should not dispatch this event globally.
26+
*/
27+
SKIP_GLOBAL_DISPATCH = 'a11ysgd',
28+
29+
/**
30+
* A property added to a dispatched event that had the MAYBE_CLICK_EVENTTYPE
31+
* event-type but could not be used as a click. The dispatcher sets this
32+
* property before it retriggers the event and it signifies that the event
33+
* contract should not look at CLICK actions for KEYDOWN events.
34+
*/
35+
SKIP_A11Y_CHECK = 'a11ysc',
36+
}
37+
38+
declare global {
39+
interface Event {
40+
[Attribute.MAYBE_CLICK_EVENT_TYPE]?: boolean;
41+
[Attribute.SKIP_GLOBAL_DISPATCH]?: boolean;
42+
[Attribute.SKIP_A11Y_CHECK]?: boolean;
43+
}
44+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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.io/license
7+
*/
8+
9+
export enum Attribute {
10+
/**
11+
* The jsaction attribute defines a mapping of a DOM event to a
12+
* generic event (aka jsaction), to which the actual event handlers
13+
* that implement the behavior of the application are bound. The
14+
* value is a semicolon separated list of colon separated pairs of
15+
* an optional DOM event name and a jsaction name. If the optional
16+
* DOM event name is omitted, 'click' is assumed. The jsaction names
17+
* are dot separated pairs of a namespace and a simple jsaction
18+
* name. If the namespace is absent, it is taken from the closest
19+
* ancestor element with a jsnamespace attribute, if there is
20+
* any. If there is no ancestor with a jsnamespace attribute, the
21+
* simple name is assumed to be the jsaction name.
22+
*
23+
* Used by EventContract.
24+
*/
25+
JSACTION = 'jsaction',
26+
27+
/**
28+
* The jsnamespace attribute provides the namespace part of the
29+
* jaction names occurring in the jsaction attribute where it's
30+
* missing.
31+
*
32+
* Used by EventContract.
33+
*/
34+
JSNAMESPACE = 'jsnamespace',
35+
36+
/**
37+
* The oi attribute is a log impression tag for impression logging
38+
* and action tracking. For an element that carries a jsaction
39+
* attribute, the element is identified for the purpose of
40+
* impression logging and click tracking by the dot separated path
41+
* of all oi attributes in the chain of ancestors of the element.
42+
*
43+
* Used by ActionFlow.
44+
*/
45+
OI = 'oi',
46+
47+
/**
48+
* The ved attribute is an encoded ClickTrackingCGI proto to track
49+
* visual elements.
50+
*
51+
* Used by ActionFlow.
52+
*/
53+
VED = 'ved',
54+
55+
/**
56+
* The vet attribute is the visual element type used to identify tracked
57+
* visual elements.
58+
*/
59+
VET = 'vet',
60+
61+
/**
62+
* Support for iteration on reprocessing.
63+
*
64+
* Used by ActionFlow.
65+
*/
66+
JSINSTANCE = 'jsinstance',
67+
68+
/**
69+
* All click jsactions that happen on the element that carries this
70+
* attribute or its descendants are automatically logged.
71+
* Impressions of jsactions on these elements are tracked too, if
72+
* requested by the impression() method of ActionFlow.
73+
*
74+
* Used by ActionFlow.
75+
*/
76+
JSTRACK = 'jstrack'
77+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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.io/license
7+
*/
8+
9+
import {Property} from './property';
10+
11+
/**
12+
* Map from jsaction annotation to a parsed map from event name to action name.
13+
*/
14+
const parseCache: {[key: string]: {[key: string]: string}} = {};
15+
16+
/**
17+
* Reads the jsaction parser cache from the given DOM Element.
18+
*
19+
* @param element .
20+
* @return Map from event to qualified name of the jsaction bound to it.
21+
*/
22+
export function get(element: Element): {[key: string]: string} {
23+
// @ts-ignore
24+
return element[Property.JSACTION];
25+
}
26+
27+
/**
28+
* Writes the jsaction parser cache to the given DOM Element.
29+
*
30+
* @param element .
31+
* @param actionMap Map from event to qualified name of the jsaction bound to
32+
* it.
33+
*/
34+
export function set(element: Element, actionMap: {[key: string]: string}) {
35+
// @ts-ignore
36+
element[Property.JSACTION] = actionMap;
37+
}
38+
39+
/**
40+
* Looks up the parsed action map from the source jsaction attribute value.
41+
*
42+
* @param text Unparsed jsaction attribute value.
43+
* @return Parsed jsaction attribute value, if already present in the cache.
44+
*/
45+
export function getParsed(text: string): {[key: string]: string}|undefined {
46+
return parseCache[text];
47+
}
48+
49+
/**
50+
* Inserts the parse result for the given source jsaction value into the cache.
51+
*
52+
* @param text Unparsed jsaction attribute value.
53+
* @param parsed Attribute value parsed into the action map.
54+
*/
55+
export function setParsed(text: string, parsed: {[key: string]: string}) {
56+
parseCache[text] = parsed;
57+
}
58+
59+
/**
60+
* Clears the jsaction parser cache from the given DOM Element.
61+
*
62+
* @param element .
63+
*/
64+
export function clear(element: Element) {
65+
if (Property.JSACTION in element) {
66+
delete element[Property.JSACTION];
67+
}
68+
}
69+
70+
/**
71+
* Reads the cached jsaction namespace from the given DOM
72+
* Element. Undefined means there is no cached value; null is a cached
73+
* jsnamespace attribute that's absent.
74+
*
75+
* @param element .
76+
* @return .
77+
*/
78+
export function getNamespace(element: Element): string|null|undefined {
79+
// @ts-ignore
80+
return element[Property.JSNAMESPACE];
81+
}
82+
83+
/**
84+
* Writes the cached jsaction namespace to the given DOM Element. Null
85+
* represents a jsnamespace attribute that's absent.
86+
*
87+
* @param element .
88+
* @param jsnamespace .
89+
*/
90+
export function setNamespace(element: Element, jsnamespace: string|null) {
91+
// @ts-ignore
92+
element[Property.JSNAMESPACE] = jsnamespace;
93+
}
94+
95+
/**
96+
* Clears the cached jsaction namespace from the given DOM Element.
97+
*
98+
* @param element .
99+
*/
100+
export function clearNamespace(element: Element) {
101+
if (Property.JSNAMESPACE in element) {
102+
delete element[Property.JSNAMESPACE];
103+
}
104+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.io/license
7+
*/
8+
9+
export const Char = {
10+
/**
11+
* The separator between the namespace and the action name in the
12+
* jsaction attribute value.
13+
*/
14+
NAMESPACE_ACTION_SEPARATOR: '.',
15+
16+
/**
17+
* The separator between the event name and action in the jsaction
18+
* attribute value.
19+
*/
20+
EVENT_ACTION_SEPARATOR: ':',
21+
22+
/**
23+
* The separator between the logged oi attribute values in the &oi=
24+
* URL parameter value.
25+
*/
26+
OI_SEPARATOR: '.',
27+
28+
/**
29+
* The separator between the key and the value pairs in the &cad=
30+
* URL parameter value.
31+
*/
32+
CAD_KEY_VALUE_SEPARATOR: ':',
33+
34+
/**
35+
* The separator between the key-value pairs in the &cad= URL
36+
* parameter value.
37+
*/
38+
CAD_SEPARATOR: ',',
39+
};

0 commit comments

Comments
 (0)