@material/web 1.1.0 → 1.1.2-nightly.035d155.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/button/internal/button.js +1 -1
- package/button/internal/button.js.map +1 -1
- package/checkbox/internal/checkbox.js +2 -1
- package/checkbox/internal/checkbox.js.map +1 -1
- package/chips/internal/filter-chip.js +1 -1
- package/chips/internal/filter-chip.js.map +1 -1
- package/dialog/internal/dialog.d.ts +3 -6
- package/dialog/internal/dialog.js +57 -6
- package/dialog/internal/dialog.js.map +1 -1
- package/field/internal/field.js +7 -2
- package/field/internal/field.js.map +1 -1
- package/internal/events/dispatch-hooks.d.ts +85 -0
- package/internal/events/dispatch-hooks.js +151 -0
- package/internal/events/dispatch-hooks.js.map +1 -0
- package/internal/{controller/events.d.ts → events/form-label-activation.d.ts} +0 -22
- package/internal/{controller/events.js → events/form-label-activation.js} +1 -35
- package/internal/events/form-label-activation.js.map +1 -0
- package/internal/events/redispatch-event.d.ts +27 -0
- package/internal/events/redispatch-event.js +40 -0
- package/internal/events/redispatch-event.js.map +1 -0
- package/labs/badge/internal/badge.d.ts +1 -1
- package/labs/badge/internal/badge.js +1 -1
- package/labs/badge/internal/badge.js.map +1 -1
- package/labs/behaviors/form-associated.js +31 -11
- package/labs/behaviors/form-associated.js.map +1 -1
- package/labs/behaviors/validators/text-field-validator.js +12 -4
- package/labs/behaviors/validators/text-field-validator.js.map +1 -1
- package/labs/card/internal/_outlined-card.scss +3 -2
- package/labs/card/internal/_shared.scss +18 -4
- package/labs/card/internal/card.js +2 -1
- package/labs/card/internal/card.js.map +1 -1
- package/labs/card/internal/outlined-styles.css.js +1 -1
- package/labs/card/internal/outlined-styles.css.js.map +1 -1
- package/labs/card/internal/shared-styles.css.js +1 -1
- package/labs/card/internal/shared-styles.css.js.map +1 -1
- package/labs/navigationbar/internal/navigation-bar.d.ts +1 -1
- package/labs/navigationbar/internal/navigation-bar.js +1 -1
- package/labs/navigationbar/internal/navigation-bar.js.map +1 -1
- package/labs/navigationdrawer/internal/_navigation-drawer-modal.scss +1 -1
- package/labs/navigationdrawer/internal/_navigation-drawer.scss +1 -1
- package/labs/navigationdrawer/internal/navigation-drawer-modal.d.ts +1 -1
- package/labs/navigationdrawer/internal/navigation-drawer-modal.js +1 -1
- package/labs/navigationdrawer/internal/navigation-drawer-modal.js.map +1 -1
- package/labs/navigationdrawer/internal/navigation-drawer.d.ts +1 -1
- package/labs/navigationdrawer/internal/navigation-drawer.js +1 -1
- package/labs/navigationdrawer/internal/navigation-drawer.js.map +1 -1
- package/labs/navigationtab/internal/navigation-tab.d.ts +1 -1
- package/labs/navigationtab/internal/navigation-tab.js +2 -2
- package/labs/navigationtab/internal/navigation-tab.js.map +1 -1
- package/labs/navigationtab/internal/state.d.ts +0 -6
- package/labs/navigationtab/internal/state.js.map +1 -1
- package/labs/segmentedbutton/internal/_outlined-segmented-button.scss +2 -2
- package/labs/segmentedbutton/internal/_shared.scss +1 -1
- package/labs/segmentedbutton/internal/outlined-segmented-button.d.ts +1 -1
- package/labs/segmentedbutton/internal/outlined-segmented-button.js +1 -1
- package/labs/segmentedbutton/internal/outlined-segmented-button.js.map +1 -1
- package/labs/segmentedbuttonset/internal/outlined-segmented-button-set.d.ts +1 -1
- package/labs/segmentedbuttonset/internal/outlined-segmented-button-set.js +1 -1
- package/labs/segmentedbuttonset/internal/outlined-segmented-button-set.js.map +1 -1
- package/menu/internal/_menu.scss +1 -0
- package/menu/internal/menu-styles.css.js +1 -1
- package/menu/internal/menu-styles.css.js.map +1 -1
- package/package.json +23 -9
- package/progress/internal/_linear-progress.scss +6 -4
- package/progress/internal/linear-progress-styles.css.js +1 -1
- package/progress/internal/linear-progress-styles.css.js.map +1 -1
- package/progress/internal/linear-progress.js +4 -1
- package/progress/internal/linear-progress.js.map +1 -1
- package/radio/internal/radio.js +1 -1
- package/radio/internal/radio.js.map +1 -1
- package/ripple/internal/ripple.js +11 -0
- package/ripple/internal/ripple.js.map +1 -1
- package/select/internal/_shared.scss +10 -1
- package/select/internal/select.d.ts +7 -1
- package/select/internal/select.js +68 -26
- package/select/internal/select.js.map +1 -1
- package/select/internal/selectoption/select-option.d.ts +26 -1
- package/select/internal/selectoption/select-option.js.map +1 -1
- package/select/internal/selectoption/selectOptionController.d.ts +2 -26
- package/select/internal/selectoption/selectOptionController.js.map +1 -1
- package/select/internal/shared-styles.css.js +1 -1
- package/select/internal/shared-styles.css.js.map +1 -1
- package/select/internal/shared.d.ts +1 -1
- package/select/internal/shared.js.map +1 -1
- package/select/select-option.d.ts +1 -0
- package/select/select-option.js.map +1 -1
- package/slider/internal/_slider.scss +31 -33
- package/slider/internal/slider-styles.css.js +1 -1
- package/slider/internal/slider-styles.css.js.map +1 -1
- package/slider/internal/slider.js +2 -1
- package/slider/internal/slider.js.map +1 -1
- package/switch/internal/switch.js +2 -1
- package/switch/internal/switch.js.map +1 -1
- package/tabs/internal/_tab.scss +7 -5
- package/tabs/internal/tab-styles.css.js +1 -1
- package/tabs/internal/tab-styles.css.js.map +1 -1
- package/textfield/internal/_input.scss +1 -1
- package/textfield/internal/shared-styles.css.js +1 -1
- package/textfield/internal/shared-styles.css.js.map +1 -1
- package/textfield/internal/text-field.d.ts +26 -0
- package/textfield/internal/text-field.js +35 -5
- package/textfield/internal/text-field.js.map +1 -1
- package/tokens/_md-comp-elevated-button.scss +1 -1
- package/tokens/_md-comp-filled-button.scss +1 -1
- package/tokens/_md-comp-filled-tonal-button.scss +1 -1
- package/tokens/_md-comp-outlined-button.scss +1 -1
- package/tokens/_md-comp-text-button.scss +1 -1
- package/internal/controller/events.js.map +0 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Add a hook for an event that is called after the event is dispatched and
|
|
8
|
+
* propagates to other event listeners.
|
|
9
|
+
*
|
|
10
|
+
* This is useful for behaviors that need to check if an event is canceled.
|
|
11
|
+
*
|
|
12
|
+
* The callback is invoked synchronously, which allows for better integration
|
|
13
|
+
* with synchronous platform APIs (like `<form>` or `<label>` clicking).
|
|
14
|
+
*
|
|
15
|
+
* Note: `setupDispatchHooks()` must be called on the element before adding any
|
|
16
|
+
* other event listeners. Call it in the constructor of an element or
|
|
17
|
+
* controller.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* class MyControl extends LitElement {
|
|
22
|
+
* constructor() {
|
|
23
|
+
* super();
|
|
24
|
+
* setupDispatchHooks(this, 'click');
|
|
25
|
+
* this.addEventListener('click', event => {
|
|
26
|
+
* afterDispatch(event, () => {
|
|
27
|
+
* if (event.defaultPrevented) {
|
|
28
|
+
* return
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* // ... perform logic
|
|
32
|
+
* });
|
|
33
|
+
* });
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* class MyController implements ReactiveController {
|
|
41
|
+
* constructor(host: ReactiveElement) {
|
|
42
|
+
* // setupDispatchHooks() may be called multiple times for the same
|
|
43
|
+
* // element and events, making it safe for multiple controllers to use it.
|
|
44
|
+
* setupDispatchHooks(host, 'click');
|
|
45
|
+
* host.addEventListener('click', event => {
|
|
46
|
+
* afterDispatch(event, () => {
|
|
47
|
+
* if (event.defaultPrevented) {
|
|
48
|
+
* return;
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* // ... perform logic
|
|
52
|
+
* });
|
|
53
|
+
* });
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @param event The event to add a hook to.
|
|
59
|
+
* @param callback A hook that is called after the event finishes dispatching.
|
|
60
|
+
*/
|
|
61
|
+
export declare function afterDispatch(event: Event, callback: () => void): void;
|
|
62
|
+
/**
|
|
63
|
+
* Sets up an element to add dispatch hooks to given event types. This must be
|
|
64
|
+
* called before adding any event listeners that need to use dispatch hooks
|
|
65
|
+
* like `afterDispatch()`.
|
|
66
|
+
*
|
|
67
|
+
* This function is safe to call multiple times with the same element or event
|
|
68
|
+
* types. Call it in the constructor of elements, mixins, and controllers to
|
|
69
|
+
* ensure it is set up before external listeners.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* class MyControl extends LitElement {
|
|
74
|
+
* constructor() {
|
|
75
|
+
* super();
|
|
76
|
+
* setupDispatchHooks(this, 'click');
|
|
77
|
+
* this.addEventListener('click', this.listenerUsingAfterDispatch);
|
|
78
|
+
* }
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @param element The element to set up event dispatch hooks for.
|
|
83
|
+
* @param eventTypes The event types to add dispatch hooks to.
|
|
84
|
+
*/
|
|
85
|
+
export declare function setupDispatchHooks(element: Element, ...eventTypes: [string, ...string[]]): void;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* A symbol used to access dispatch hooks on an event.
|
|
8
|
+
*/
|
|
9
|
+
const dispatchHooks = Symbol('dispatchHooks');
|
|
10
|
+
/**
|
|
11
|
+
* Add a hook for an event that is called after the event is dispatched and
|
|
12
|
+
* propagates to other event listeners.
|
|
13
|
+
*
|
|
14
|
+
* This is useful for behaviors that need to check if an event is canceled.
|
|
15
|
+
*
|
|
16
|
+
* The callback is invoked synchronously, which allows for better integration
|
|
17
|
+
* with synchronous platform APIs (like `<form>` or `<label>` clicking).
|
|
18
|
+
*
|
|
19
|
+
* Note: `setupDispatchHooks()` must be called on the element before adding any
|
|
20
|
+
* other event listeners. Call it in the constructor of an element or
|
|
21
|
+
* controller.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* class MyControl extends LitElement {
|
|
26
|
+
* constructor() {
|
|
27
|
+
* super();
|
|
28
|
+
* setupDispatchHooks(this, 'click');
|
|
29
|
+
* this.addEventListener('click', event => {
|
|
30
|
+
* afterDispatch(event, () => {
|
|
31
|
+
* if (event.defaultPrevented) {
|
|
32
|
+
* return
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // ... perform logic
|
|
36
|
+
* });
|
|
37
|
+
* });
|
|
38
|
+
* }
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* class MyController implements ReactiveController {
|
|
45
|
+
* constructor(host: ReactiveElement) {
|
|
46
|
+
* // setupDispatchHooks() may be called multiple times for the same
|
|
47
|
+
* // element and events, making it safe for multiple controllers to use it.
|
|
48
|
+
* setupDispatchHooks(host, 'click');
|
|
49
|
+
* host.addEventListener('click', event => {
|
|
50
|
+
* afterDispatch(event, () => {
|
|
51
|
+
* if (event.defaultPrevented) {
|
|
52
|
+
* return;
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* // ... perform logic
|
|
56
|
+
* });
|
|
57
|
+
* });
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @param event The event to add a hook to.
|
|
63
|
+
* @param callback A hook that is called after the event finishes dispatching.
|
|
64
|
+
*/
|
|
65
|
+
export function afterDispatch(event, callback) {
|
|
66
|
+
const hooks = event[dispatchHooks];
|
|
67
|
+
if (!hooks) {
|
|
68
|
+
throw new Error(`'${event.type}' event needs setupDispatchHooks().`);
|
|
69
|
+
}
|
|
70
|
+
hooks.addEventListener('after', callback);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A lookup map of elements and event types that have a dispatch hook listener
|
|
74
|
+
* set up. Used to ensure we don't set up multiple hook listeners on the same
|
|
75
|
+
* element for the same event.
|
|
76
|
+
*/
|
|
77
|
+
const ELEMENT_DISPATCH_HOOK_TYPES = new WeakMap();
|
|
78
|
+
/**
|
|
79
|
+
* Sets up an element to add dispatch hooks to given event types. This must be
|
|
80
|
+
* called before adding any event listeners that need to use dispatch hooks
|
|
81
|
+
* like `afterDispatch()`.
|
|
82
|
+
*
|
|
83
|
+
* This function is safe to call multiple times with the same element or event
|
|
84
|
+
* types. Call it in the constructor of elements, mixins, and controllers to
|
|
85
|
+
* ensure it is set up before external listeners.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* class MyControl extends LitElement {
|
|
90
|
+
* constructor() {
|
|
91
|
+
* super();
|
|
92
|
+
* setupDispatchHooks(this, 'click');
|
|
93
|
+
* this.addEventListener('click', this.listenerUsingAfterDispatch);
|
|
94
|
+
* }
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @param element The element to set up event dispatch hooks for.
|
|
99
|
+
* @param eventTypes The event types to add dispatch hooks to.
|
|
100
|
+
*/
|
|
101
|
+
export function setupDispatchHooks(element, ...eventTypes) {
|
|
102
|
+
let typesAlreadySetUp = ELEMENT_DISPATCH_HOOK_TYPES.get(element);
|
|
103
|
+
if (!typesAlreadySetUp) {
|
|
104
|
+
typesAlreadySetUp = new Set();
|
|
105
|
+
ELEMENT_DISPATCH_HOOK_TYPES.set(element, typesAlreadySetUp);
|
|
106
|
+
}
|
|
107
|
+
for (const eventType of eventTypes) {
|
|
108
|
+
// Don't register multiple dispatch hook listeners. A second registration
|
|
109
|
+
// would lead to the second listener re-dispatching a re-dispatched event,
|
|
110
|
+
// which can cause an infinite loop inside the other one.
|
|
111
|
+
if (typesAlreadySetUp.has(eventType)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
// When we re-dispatch the event, it's going to immediately trigger this
|
|
115
|
+
// listener again. Use a flag to ignore it.
|
|
116
|
+
let isRedispatching = false;
|
|
117
|
+
element.addEventListener(eventType, (event) => {
|
|
118
|
+
if (isRedispatching) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Do not let the event propagate to any other listener (not just
|
|
122
|
+
// bubbling listeners with `stopPropagation()`).
|
|
123
|
+
event.stopImmediatePropagation();
|
|
124
|
+
// Make a copy.
|
|
125
|
+
const eventCopy = Reflect.construct(event.constructor, [
|
|
126
|
+
event.type,
|
|
127
|
+
event,
|
|
128
|
+
]);
|
|
129
|
+
// Add hooks onto the event.
|
|
130
|
+
const hooks = new EventTarget();
|
|
131
|
+
eventCopy[dispatchHooks] = hooks;
|
|
132
|
+
// Re-dispatch the event. We can't reuse `redispatchEvent()` since we
|
|
133
|
+
// need to add the hooks to the copy before it's dispatched.
|
|
134
|
+
isRedispatching = true;
|
|
135
|
+
const dispatched = element.dispatchEvent(eventCopy);
|
|
136
|
+
isRedispatching = false;
|
|
137
|
+
if (!dispatched) {
|
|
138
|
+
event.preventDefault();
|
|
139
|
+
}
|
|
140
|
+
// Synchronously call afterDispatch() hooks.
|
|
141
|
+
hooks.dispatchEvent(new Event('after'));
|
|
142
|
+
}, {
|
|
143
|
+
// Ensure this listener runs before other listeners.
|
|
144
|
+
// `setupDispatchHooks()` should be called in constructors to also
|
|
145
|
+
// ensure they run before any other externally-added capture listeners.
|
|
146
|
+
capture: true,
|
|
147
|
+
});
|
|
148
|
+
typesAlreadySetUp.add(eventType);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=dispatch-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch-hooks.js","sourceRoot":"","sources":["dispatch-hooks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,aAAa,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;AAS9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,UAAU,aAAa,CAAC,KAAY,EAAE,QAAoB;IAC9D,MAAM,KAAK,GAAI,KAAgC,CAAC,aAAa,CAAC,CAAC;IAC/D,IAAI,CAAC,KAAK,EAAE;QACV,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,qCAAqC,CAAC,CAAC;KACtE;IAED,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,2BAA2B,GAAG,IAAI,OAAO,EAAwB,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAgB,EAChB,GAAG,UAAiC;IAEpC,IAAI,iBAAiB,GAAG,2BAA2B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACjE,IAAI,CAAC,iBAAiB,EAAE;QACtB,iBAAiB,GAAG,IAAI,GAAG,EAAE,CAAC;QAC9B,2BAA2B,CAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;KAC7D;IAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;QAClC,yEAAyE;QACzE,0EAA0E;QAC1E,yDAAyD;QACzD,IAAI,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;YACpC,SAAS;SACV;QAED,wEAAwE;QACxE,2CAA2C;QAC3C,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,OAAO,CAAC,gBAAgB,CACtB,SAAS,EACT,CAAC,KAAY,EAAE,EAAE;YACf,IAAI,eAAe,EAAE;gBACnB,OAAO;aACR;YAED,iEAAiE;YACjE,gDAAgD;YAChD,KAAK,CAAC,wBAAwB,EAAE,CAAC;YACjC,eAAe;YACf,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE;gBACrD,KAAK,CAAC,IAAI;gBACV,KAAK;aACN,CAAC,CAAC;YAEH,4BAA4B;YAC5B,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC;YAC/B,SAAoC,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC;YAE7D,qEAAqE;YACrE,4DAA4D;YAC5D,eAAe,GAAG,IAAI,CAAC;YACvB,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YACpD,eAAe,GAAG,KAAK,CAAC;YACxB,IAAI,CAAC,UAAU,EAAE;gBACf,KAAK,CAAC,cAAc,EAAE,CAAC;aACxB;YAED,4CAA4C;YAC5C,KAAK,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1C,CAAC,EACD;YACE,oDAAoD;YACpD,kEAAkE;YAClE,uEAAuE;YACvE,OAAO,EAAE,IAAI;SACd,CACF,CAAC;QAEF,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAClC;AACH,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * A symbol used to access dispatch hooks on an event.\n */\nconst dispatchHooks = Symbol('dispatchHooks');\n\n/**\n * An `Event` with additional symbols for dispatch hooks.\n */\ninterface EventWithDispatchHooks extends Event {\n [dispatchHooks]: EventTarget;\n}\n\n/**\n * Add a hook for an event that is called after the event is dispatched and\n * propagates to other event listeners.\n *\n * This is useful for behaviors that need to check if an event is canceled.\n *\n * The callback is invoked synchronously, which allows for better integration\n * with synchronous platform APIs (like `<form>` or `<label>` clicking).\n *\n * Note: `setupDispatchHooks()` must be called on the element before adding any\n * other event listeners. Call it in the constructor of an element or\n * controller.\n *\n * @example\n * ```ts\n * class MyControl extends LitElement {\n * constructor() {\n * super();\n * setupDispatchHooks(this, 'click');\n * this.addEventListener('click', event => {\n * afterDispatch(event, () => {\n * if (event.defaultPrevented) {\n * return\n * }\n *\n * // ... perform logic\n * });\n * });\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * class MyController implements ReactiveController {\n * constructor(host: ReactiveElement) {\n * // setupDispatchHooks() may be called multiple times for the same\n * // element and events, making it safe for multiple controllers to use it.\n * setupDispatchHooks(host, 'click');\n * host.addEventListener('click', event => {\n * afterDispatch(event, () => {\n * if (event.defaultPrevented) {\n * return;\n * }\n *\n * // ... perform logic\n * });\n * });\n * }\n * }\n * ```\n *\n * @param event The event to add a hook to.\n * @param callback A hook that is called after the event finishes dispatching.\n */\nexport function afterDispatch(event: Event, callback: () => void) {\n const hooks = (event as EventWithDispatchHooks)[dispatchHooks];\n if (!hooks) {\n throw new Error(`'${event.type}' event needs setupDispatchHooks().`);\n }\n\n hooks.addEventListener('after', callback);\n}\n\n/**\n * A lookup map of elements and event types that have a dispatch hook listener\n * set up. Used to ensure we don't set up multiple hook listeners on the same\n * element for the same event.\n */\nconst ELEMENT_DISPATCH_HOOK_TYPES = new WeakMap<Element, Set<string>>();\n\n/**\n * Sets up an element to add dispatch hooks to given event types. This must be\n * called before adding any event listeners that need to use dispatch hooks\n * like `afterDispatch()`.\n *\n * This function is safe to call multiple times with the same element or event\n * types. Call it in the constructor of elements, mixins, and controllers to\n * ensure it is set up before external listeners.\n *\n * @example\n * ```ts\n * class MyControl extends LitElement {\n * constructor() {\n * super();\n * setupDispatchHooks(this, 'click');\n * this.addEventListener('click', this.listenerUsingAfterDispatch);\n * }\n * }\n * ```\n *\n * @param element The element to set up event dispatch hooks for.\n * @param eventTypes The event types to add dispatch hooks to.\n */\nexport function setupDispatchHooks(\n element: Element,\n ...eventTypes: [string, ...string[]]\n) {\n let typesAlreadySetUp = ELEMENT_DISPATCH_HOOK_TYPES.get(element);\n if (!typesAlreadySetUp) {\n typesAlreadySetUp = new Set();\n ELEMENT_DISPATCH_HOOK_TYPES.set(element, typesAlreadySetUp);\n }\n\n for (const eventType of eventTypes) {\n // Don't register multiple dispatch hook listeners. A second registration\n // would lead to the second listener re-dispatching a re-dispatched event,\n // which can cause an infinite loop inside the other one.\n if (typesAlreadySetUp.has(eventType)) {\n continue;\n }\n\n // When we re-dispatch the event, it's going to immediately trigger this\n // listener again. Use a flag to ignore it.\n let isRedispatching = false;\n element.addEventListener(\n eventType,\n (event: Event) => {\n if (isRedispatching) {\n return;\n }\n\n // Do not let the event propagate to any other listener (not just\n // bubbling listeners with `stopPropagation()`).\n event.stopImmediatePropagation();\n // Make a copy.\n const eventCopy = Reflect.construct(event.constructor, [\n event.type,\n event,\n ]);\n\n // Add hooks onto the event.\n const hooks = new EventTarget();\n (eventCopy as EventWithDispatchHooks)[dispatchHooks] = hooks;\n\n // Re-dispatch the event. We can't reuse `redispatchEvent()` since we\n // need to add the hooks to the copy before it's dispatched.\n isRedispatching = true;\n const dispatched = element.dispatchEvent(eventCopy);\n isRedispatching = false;\n if (!dispatched) {\n event.preventDefault();\n }\n\n // Synchronously call afterDispatch() hooks.\n hooks.dispatchEvent(new Event('after'));\n },\n {\n // Ensure this listener runs before other listeners.\n // `setupDispatchHooks()` should be called in constructors to also\n // ensure they run before any other externally-added capture listeners.\n capture: true,\n },\n );\n\n typesAlreadySetUp.add(eventType);\n }\n}\n"]}
|
|
@@ -3,28 +3,6 @@
|
|
|
3
3
|
* Copyright 2021 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
/**
|
|
7
|
-
* Re-dispatches an event from the provided element.
|
|
8
|
-
*
|
|
9
|
-
* This function is useful for forwarding non-composed events, such as `change`
|
|
10
|
-
* events.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* class MyInput extends LitElement {
|
|
14
|
-
* render() {
|
|
15
|
-
* return html`<input @change=${this.redispatchEvent}>`;
|
|
16
|
-
* }
|
|
17
|
-
*
|
|
18
|
-
* protected redispatchEvent(event: Event) {
|
|
19
|
-
* redispatchEvent(this, event);
|
|
20
|
-
* }
|
|
21
|
-
* }
|
|
22
|
-
*
|
|
23
|
-
* @param element The element to dispatch the event from.
|
|
24
|
-
* @param event The event to re-dispatch.
|
|
25
|
-
* @return Whether or not the event was dispatched (if cancelable).
|
|
26
|
-
*/
|
|
27
|
-
export declare function redispatchEvent(element: Element, event: Event): boolean;
|
|
28
6
|
/**
|
|
29
7
|
* Dispatches a click event to the given element that triggers a native action,
|
|
30
8
|
* but is not composed and therefore is not seen outside the element.
|
|
@@ -3,40 +3,6 @@
|
|
|
3
3
|
* Copyright 2021 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
/**
|
|
7
|
-
* Re-dispatches an event from the provided element.
|
|
8
|
-
*
|
|
9
|
-
* This function is useful for forwarding non-composed events, such as `change`
|
|
10
|
-
* events.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* class MyInput extends LitElement {
|
|
14
|
-
* render() {
|
|
15
|
-
* return html`<input @change=${this.redispatchEvent}>`;
|
|
16
|
-
* }
|
|
17
|
-
*
|
|
18
|
-
* protected redispatchEvent(event: Event) {
|
|
19
|
-
* redispatchEvent(this, event);
|
|
20
|
-
* }
|
|
21
|
-
* }
|
|
22
|
-
*
|
|
23
|
-
* @param element The element to dispatch the event from.
|
|
24
|
-
* @param event The event to re-dispatch.
|
|
25
|
-
* @return Whether or not the event was dispatched (if cancelable).
|
|
26
|
-
*/
|
|
27
|
-
export function redispatchEvent(element, event) {
|
|
28
|
-
// For bubbling events in SSR light DOM (or composed), stop their propagation
|
|
29
|
-
// and dispatch the copy.
|
|
30
|
-
if (event.bubbles && (!element.shadowRoot || event.composed)) {
|
|
31
|
-
event.stopPropagation();
|
|
32
|
-
}
|
|
33
|
-
const copy = Reflect.construct(event.constructor, [event.type, event]);
|
|
34
|
-
const dispatched = element.dispatchEvent(copy);
|
|
35
|
-
if (!dispatched) {
|
|
36
|
-
event.preventDefault();
|
|
37
|
-
}
|
|
38
|
-
return dispatched;
|
|
39
|
-
}
|
|
40
6
|
/**
|
|
41
7
|
* Dispatches a click event to the given element that triggers a native action,
|
|
42
8
|
* but is not composed and therefore is not seen outside the element.
|
|
@@ -127,4 +93,4 @@ async function squelchEventsForMicrotask() {
|
|
|
127
93
|
await null;
|
|
128
94
|
isSquelchingEvents = false;
|
|
129
95
|
}
|
|
130
|
-
//# sourceMappingURL=
|
|
96
|
+
//# sourceMappingURL=form-label-activation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-label-activation.js","sourceRoot":"","sources":["form-label-activation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAoB;IAC1D,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,OAAO,EAAE,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;IACvD,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAY;IAC5C,wCAAwC;IACxC,IAAI,KAAK,CAAC,aAAa,KAAK,KAAK,CAAC,MAAM,EAAE;QACxC,OAAO,KAAK,CAAC;KACd;IACD,gDAAgD;IAChD,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,MAAM,EAAE;QAC5C,OAAO,KAAK,CAAC;KACd;IACD,0EAA0E;IAC1E,oBAAoB;IACpB,IAAK,KAAK,CAAC,MAA4C,CAAC,QAAQ,EAAE;QAChE,OAAO,KAAK,CAAC;KACd;IACD,8DAA8D;IAC9D,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,6DAA6D;AAC7D,yCAAyC;AACzC,SAAS,YAAY,CAAC,KAAY;IAChC,MAAM,SAAS,GAAG,kBAAkB,CAAC;IACrC,IAAI,SAAS,EAAE;QACb,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,KAAK,CAAC,wBAAwB,EAAE,CAAC;KAClC;IACD,yBAAyB,EAAE,CAAC;IAC5B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,wCAAwC;AACxC,IAAI,kBAAkB,GAAG,KAAK,CAAC;AAC/B,KAAK,UAAU,yBAAyB;IACtC,kBAAkB,GAAG,IAAI,CAAC;IAC1B,wCAAwC;IACxC,2BAA2B;IAC3B,MAAM,IAAI,CAAC;IACX,kBAAkB,GAAG,KAAK,CAAC;AAC7B,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2021 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Dispatches a click event to the given element that triggers a native action,\n * but is not composed and therefore is not seen outside the element.\n *\n * This is useful for responding to an external click event on the host element\n * that should trigger an internal action like a button click.\n *\n * Note, a helper is provided because setting this up correctly is a bit tricky.\n * In particular, calling `click` on an element creates a composed event, which\n * is not desirable, and a manually dispatched event must specifically be a\n * `MouseEvent` to trigger a native action.\n *\n * @example\n * hostClickListener = (event: MouseEvent) {\n * if (isActivationClick(event)) {\n * this.dispatchActivationClick(this.buttonElement);\n * }\n * }\n *\n */\nexport function dispatchActivationClick(element: HTMLElement) {\n const event = new MouseEvent('click', {bubbles: true});\n element.dispatchEvent(event);\n return event;\n}\n\n/**\n * Returns true if the click event should trigger an activation behavior. The\n * behavior is defined by the element and is whatever it should do when\n * clicked.\n *\n * Typically when an element needs to handle a click, the click is generated\n * from within the element and an event listener within the element implements\n * the needed behavior; however, it's possible to fire a click directly\n * at the element that the element should handle. This method helps\n * distinguish these \"external\" clicks.\n *\n * An \"external\" click can be triggered in a number of ways: via a click\n * on an associated label for a form associated element, calling\n * `element.click()`, or calling\n * `element.dispatchEvent(new MouseEvent('click', ...))`.\n *\n * Also works around Firefox issue\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1804576 by squelching\n * events for a microtask after called.\n *\n * @example\n * hostClickListener = (event: MouseEvent) {\n * if (isActivationClick(event)) {\n * this.dispatchActivationClick(this.buttonElement);\n * }\n * }\n *\n */\nexport function isActivationClick(event: Event) {\n // Event must start at the event target.\n if (event.currentTarget !== event.target) {\n return false;\n }\n // Event must not be retargeted from shadowRoot.\n if (event.composedPath()[0] !== event.target) {\n return false;\n }\n // Target must not be disabled; this should only occur for a synthetically\n // dispatched click.\n if ((event.target as EventTarget & {disabled: boolean}).disabled) {\n return false;\n }\n // This is an activation if the event should not be squelched.\n return !squelchEvent(event);\n}\n\n// TODO(https://bugzilla.mozilla.org/show_bug.cgi?id=1804576)\n// Remove when Firefox bug is addressed.\nfunction squelchEvent(event: Event) {\n const squelched = isSquelchingEvents;\n if (squelched) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n squelchEventsForMicrotask();\n return squelched;\n}\n\n// Ignore events for one microtask only.\nlet isSquelchingEvents = false;\nasync function squelchEventsForMicrotask() {\n isSquelchingEvents = true;\n // Need to pause for just one microtask.\n // tslint:disable-next-line\n await null;\n isSquelchingEvents = false;\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2021 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Re-dispatches an event from the provided element.
|
|
8
|
+
*
|
|
9
|
+
* This function is useful for forwarding non-composed events, such as `change`
|
|
10
|
+
* events.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* class MyInput extends LitElement {
|
|
14
|
+
* render() {
|
|
15
|
+
* return html`<input @change=${this.redispatchEvent}>`;
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* protected redispatchEvent(event: Event) {
|
|
19
|
+
* redispatchEvent(this, event);
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* @param element The element to dispatch the event from.
|
|
24
|
+
* @param event The event to re-dispatch.
|
|
25
|
+
* @return Whether or not the event was dispatched (if cancelable).
|
|
26
|
+
*/
|
|
27
|
+
export declare function redispatchEvent(element: Element, event: Event): boolean;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2021 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Re-dispatches an event from the provided element.
|
|
8
|
+
*
|
|
9
|
+
* This function is useful for forwarding non-composed events, such as `change`
|
|
10
|
+
* events.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* class MyInput extends LitElement {
|
|
14
|
+
* render() {
|
|
15
|
+
* return html`<input @change=${this.redispatchEvent}>`;
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* protected redispatchEvent(event: Event) {
|
|
19
|
+
* redispatchEvent(this, event);
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* @param element The element to dispatch the event from.
|
|
24
|
+
* @param event The event to re-dispatch.
|
|
25
|
+
* @return Whether or not the event was dispatched (if cancelable).
|
|
26
|
+
*/
|
|
27
|
+
export function redispatchEvent(element, event) {
|
|
28
|
+
// For bubbling events in SSR light DOM (or composed), stop their propagation
|
|
29
|
+
// and dispatch the copy.
|
|
30
|
+
if (event.bubbles && (!element.shadowRoot || event.composed)) {
|
|
31
|
+
event.stopPropagation();
|
|
32
|
+
}
|
|
33
|
+
const copy = Reflect.construct(event.constructor, [event.type, event]);
|
|
34
|
+
const dispatched = element.dispatchEvent(copy);
|
|
35
|
+
if (!dispatched) {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
}
|
|
38
|
+
return dispatched;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=redispatch-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redispatch-event.js","sourceRoot":"","sources":["redispatch-event.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB,EAAE,KAAY;IAC5D,6EAA6E;IAC7E,yBAAyB;IACzB,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE;QAC5D,KAAK,CAAC,eAAe,EAAE,CAAC;KACzB;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,EAAE;QACf,KAAK,CAAC,cAAc,EAAE,CAAC;KACxB;IAED,OAAO,UAAU,CAAC;AACpB,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2021 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Re-dispatches an event from the provided element.\n *\n * This function is useful for forwarding non-composed events, such as `change`\n * events.\n *\n * @example\n * class MyInput extends LitElement {\n * render() {\n * return html`<input @change=${this.redispatchEvent}>`;\n * }\n *\n * protected redispatchEvent(event: Event) {\n * redispatchEvent(this, event);\n * }\n * }\n *\n * @param element The element to dispatch the event from.\n * @param event The event to re-dispatch.\n * @return Whether or not the event was dispatched (if cancelable).\n */\nexport function redispatchEvent(element: Element, event: Event) {\n // For bubbling events in SSR light DOM (or composed), stop their propagation\n // and dispatch the copy.\n if (event.bubbles && (!element.shadowRoot || event.composed)) {\n event.stopPropagation();\n }\n\n const copy = Reflect.construct(event.constructor, [event.type, event]);\n const dispatched = element.dispatchEvent(copy);\n if (!dispatched) {\n event.preventDefault();\n }\n\n return dispatched;\n}\n"]}
|
|
@@ -8,7 +8,7 @@ import { html, LitElement } from 'lit';
|
|
|
8
8
|
import { property } from 'lit/decorators.js';
|
|
9
9
|
import { classMap } from 'lit/directives/class-map.js';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* b/265340196 - add docs
|
|
12
12
|
*/
|
|
13
13
|
export class Badge extends LitElement {
|
|
14
14
|
constructor() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"badge.js","sourceRoot":"","sources":["badge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;AAEH,OAAO,EAAC,IAAI,EAAE,UAAU,EAAC,MAAM,KAAK,CAAC;AACrC,OAAO,EAAC,QAAQ,EAAC,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAC,QAAQ,EAAC,MAAM,6BAA6B,CAAC;AAErD;;GAEG;AACH,MAAM,OAAO,KAAM,SAAQ,UAAU;IAArC;;QACc,UAAK,GAAG,EAAE,CAAC;IASzB,CAAC;IAPoB,MAAM;QACvB,MAAM,OAAO,GAAG,EAAC,kBAAkB,EAAE,IAAI,CAAC,KAAK,EAAC,CAAC;QAEjD,OAAO,IAAI,CAAA,yBAAyB,QAAQ,CAAC,OAAO,CAAC;oCACrB,IAAI,CAAC,KAAK;WACnC,CAAC;IACV,CAAC;CACF;AATa;IAAX,QAAQ,EAAE;oCAAY","sourcesContent":["/**\n * @license\n * Copyright 2022 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {html, LitElement} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport {classMap} from 'lit/directives/class-map.js';\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"badge.js","sourceRoot":"","sources":["badge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;AAEH,OAAO,EAAC,IAAI,EAAE,UAAU,EAAC,MAAM,KAAK,CAAC;AACrC,OAAO,EAAC,QAAQ,EAAC,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAC,QAAQ,EAAC,MAAM,6BAA6B,CAAC;AAErD;;GAEG;AACH,MAAM,OAAO,KAAM,SAAQ,UAAU;IAArC;;QACc,UAAK,GAAG,EAAE,CAAC;IASzB,CAAC;IAPoB,MAAM;QACvB,MAAM,OAAO,GAAG,EAAC,kBAAkB,EAAE,IAAI,CAAC,KAAK,EAAC,CAAC;QAEjD,OAAO,IAAI,CAAA,yBAAyB,QAAQ,CAAC,OAAO,CAAC;oCACrB,IAAI,CAAC,KAAK;WACnC,CAAC;IACV,CAAC;CACF;AATa;IAAX,QAAQ,EAAE;oCAAY","sourcesContent":["/**\n * @license\n * Copyright 2022 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {html, LitElement} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport {classMap} from 'lit/directives/class-map.js';\n\n/**\n * b/265340196 - add docs\n */\nexport class Badge extends LitElement {\n @property() value = '';\n\n protected override render() {\n const classes = {'md3-badge--large': this.value};\n\n return html`<div class=\"md3-badge ${classMap(classes)}\">\n <p class=\"md3-badge__value\">${this.value}</p>\n </div>`;\n }\n}\n"]}
|
|
@@ -100,26 +100,46 @@ export function mixinFormAssociated(base) {
|
|
|
100
100
|
get labels() {
|
|
101
101
|
return this[internals].labels;
|
|
102
102
|
}
|
|
103
|
-
// name
|
|
103
|
+
// Use @property for the `name` and `disabled` properties to add them to the
|
|
104
|
+
// `observedAttributes` array and trigger `attributeChangedCallback()`.
|
|
105
|
+
//
|
|
106
|
+
// We don't use Lit's default getter/setter (`noAccessor: true`) because
|
|
107
|
+
// the attributes need to be updated synchronously to work with synchronous
|
|
108
|
+
// form APIs, and Lit updates attributes async by default.
|
|
104
109
|
get name() {
|
|
105
110
|
return this.getAttribute('name') ?? '';
|
|
106
111
|
}
|
|
107
112
|
set name(name) {
|
|
108
|
-
|
|
109
|
-
// Setting name to null or empty string does not remove the attribute.
|
|
113
|
+
// Note: setting name to null or empty does not remove the attribute.
|
|
110
114
|
this.setAttribute('name', name);
|
|
111
|
-
//
|
|
112
|
-
|
|
115
|
+
// We don't need to call `requestUpdate()` since it's called synchronously
|
|
116
|
+
// in `attributeChangedCallback()`.
|
|
113
117
|
}
|
|
114
|
-
// disabled attribute must be set synchronously
|
|
115
118
|
get disabled() {
|
|
116
119
|
return this.hasAttribute('disabled');
|
|
117
120
|
}
|
|
118
121
|
set disabled(disabled) {
|
|
119
|
-
const prev = this.disabled;
|
|
120
122
|
this.toggleAttribute('disabled', disabled);
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
+
// We don't need to call `requestUpdate()` since it's called synchronously
|
|
124
|
+
// in `attributeChangedCallback()`.
|
|
125
|
+
}
|
|
126
|
+
attributeChangedCallback(name, old, value) {
|
|
127
|
+
// Manually `requestUpdate()` for `name` and `disabled` when their
|
|
128
|
+
// attribute or property changes.
|
|
129
|
+
// The properties update their attributes, so this callback is invoked
|
|
130
|
+
// immediately when the properties are set. We call `requestUpdate()` here
|
|
131
|
+
// instead of letting Lit set the properties from the attribute change.
|
|
132
|
+
// That would cause the properties to re-set the attribute and invoke this
|
|
133
|
+
// callback again in a loop. This leads to stale state when Lit tries to
|
|
134
|
+
// determine if a property changed or not.
|
|
135
|
+
if (name === 'name' || name === 'disabled') {
|
|
136
|
+
// Disabled's value is only false if the attribute is missing and null.
|
|
137
|
+
const oldValue = name === 'disabled' ? old !== null : old;
|
|
138
|
+
// Trigger a lit update when the attribute changes.
|
|
139
|
+
this.requestUpdate(name, oldValue);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
super.attributeChangedCallback(name, old, value);
|
|
123
143
|
}
|
|
124
144
|
requestUpdate(name, oldValue, options) {
|
|
125
145
|
super.requestUpdate(name, oldValue, options);
|
|
@@ -145,10 +165,10 @@ export function mixinFormAssociated(base) {
|
|
|
145
165
|
/** @nocollapse */
|
|
146
166
|
FormAssociatedElement.formAssociated = true;
|
|
147
167
|
__decorate([
|
|
148
|
-
property({
|
|
168
|
+
property({ noAccessor: true })
|
|
149
169
|
], FormAssociatedElement.prototype, "name", null);
|
|
150
170
|
__decorate([
|
|
151
|
-
property({ type: Boolean,
|
|
171
|
+
property({ type: Boolean, noAccessor: true })
|
|
152
172
|
], FormAssociatedElement.prototype, "disabled", null);
|
|
153
173
|
return FormAssociatedElement;
|
|
154
174
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-associated.js","sourceRoot":"","sources":["form-associated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;AAGH,OAAO,EAAC,QAAQ,EAAC,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAAC,SAAS,EAAuB,MAAM,wBAAwB,CAAC;AA0GvE;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAEnD;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AACH,MAAM,UAAU,mBAAmB,CAEjC,IAAO;IACP,MAAe,qBAAsB,SAAQ,IAAI;QAI/C,IAAI,IAAI;YACN,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC;QAC9B,CAAC;QAED,IAAI,MAAM;YACR,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;QAChC,CAAC;QAED,2CAA2C;QAE3C,IAAI,IAAI;YACN,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;QACD,IAAI,IAAI,CAAC,IAAY;YACnB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACvB,sEAAsE;YACtE,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAChC,4CAA4C;YAC5C,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QAED,+CAA+C;QAE/C,IAAI,QAAQ;YACV,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,QAAQ,CAAC,QAAiB;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC3B,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC3C,4CAA4C;YAC5C,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACvC,CAAC;QAEQ,aAAa,CACpB,IAAkB,EAClB,QAAkB,EAClB,OAA6B;YAE7B,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC7C,0EAA0E;YAC1E,WAAW;YACX,uEAAuE;YACvE,0EAA0E;YAC1E,qEAAqE;YACrE,IAAI,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,CAAC,YAAY,CAAC;YACZ,+DAA+D;YAC/D,4BAA4B;YAC5B,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;QAED,CAAC,YAAY,CAAC;YACZ,OAAO,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,CAAC;QAED,oBAAoB,CAAC,QAAiB;YACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC3B,CAAC;;IA9DD,kBAAkB;IACF,oCAAc,GAAG,IAAI,CAAC;IAYtC;QADC,QAAQ,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;qDAGzB;IAWD;QADC,QAAQ,CAAC,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC;yDAGxC;IA4CH,OAAO,qBAAqB,CAAC;AAC/B,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {LitElement, PropertyDeclaration} from 'lit';\nimport {property} from 'lit/decorators.js';\n\nimport {internals, WithElementInternals} from './element-internals.js';\nimport {MixinBase, MixinReturn} from './mixin.js';\n\n/**\n * A form-associated element.\n *\n * IMPORTANT: Requires declares for lit-analyzer\n * @example\n * ```ts\n * const base = mixinFormAssociated(mixinElementInternals(LitElement));\n * class MyControl extends base {\n * // Writable mixin properties for lit-html binding, needed for lit-analyzer\n * declare disabled: boolean;\n * declare name: string;\n * }\n * ```\n */\nexport interface FormAssociated {\n /**\n * The associated form element with which this element's value will submit.\n */\n readonly form: HTMLFormElement | null;\n\n /**\n * The labels this element is associated with.\n */\n readonly labels: NodeList;\n\n /**\n * The HTML name to use in form submission.\n */\n name: string;\n\n /**\n * Whether or not the element is disabled.\n */\n disabled: boolean;\n\n /**\n * Gets the current form value of a component.\n *\n * @return The current form value.\n */\n [getFormValue](): FormValue | null;\n\n /**\n * Gets the current form state of a component. Defaults to the component's\n * `[formValue]`.\n *\n * Use this when the state of an element is different from its value, such as\n * checkboxes (internal boolean state and a user string value).\n *\n * @return The current form state, defaults to the form value.\n */\n [getFormState](): FormValue | null;\n\n /**\n * A callback for when a form component should be disabled or enabled. This\n * can be called in a variety of situations, such as disabled `<fieldset>`s.\n *\n * @param disabled Whether or not the form control should be disabled.\n */\n formDisabledCallback(disabled: boolean): void;\n\n /**\n * A callback for when the form requests to reset its value. Typically, the\n * default value that is reset is represented in the attribute of an element.\n *\n * This means the attribute used for the value should not update as the value\n * changes. For example, a checkbox should not change its default `checked`\n * attribute when selected. Ensure form values do not reflect.\n */\n formResetCallback(): void;\n\n /**\n * A callback for when the form restores the state of a component. For\n * example, when a page is reloaded or forms are autofilled.\n *\n * @param state The state to restore, or null to reset the form control's\n * value.\n * @param reason The reason state was restored, either `'restore'` or\n * `'autocomplete'`.\n */\n formStateRestoreCallback(\n state: FormRestoreState | null,\n reason: FormRestoreReason,\n ): void;\n\n /**\n * An optional callback for when the associated form changes.\n *\n * @param form The new associated form, or `null` if there is none.\n */\n formAssociatedCallback?(form: HTMLFormElement | null): void;\n}\n\n/**\n * The constructor of a `FormAssociated` element.\n */\nexport interface FormAssociatedConstructor {\n /**\n * Indicates that an element is participating in form association.\n */\n readonly formAssociated: true;\n}\n\n/**\n * A symbol property to retrieve the form value for an element.\n */\nexport const getFormValue = Symbol('getFormValue');\n\n/**\n * A symbol property to retrieve the form state for an element.\n */\nexport const getFormState = Symbol('getFormState');\n\n/**\n * Mixes in form-associated behavior for a class. This allows an element to add\n * values to `<form>` elements.\n *\n * Implementing classes should provide a `[formValue]` to return the current\n * value of the element, as well as reset and restore callbacks.\n *\n * @example\n * ```ts\n * const base = mixinFormAssociated(mixinElementInternals(LitElement));\n *\n * class MyControl extends base {\n * \\@property()\n * value = '';\n *\n * override [getFormValue]() {\n * return this.value;\n * }\n *\n * override formResetCallback() {\n * const defaultValue = this.getAttribute('value');\n * this.value = defaultValue;\n * }\n *\n * override formStateRestoreCallback(state: string) {\n * this.value = state;\n * }\n * }\n * ```\n *\n * Elements may optionally provide a `[formState]` if their values do not\n * represent the state of the component.\n *\n * @example\n * ```ts\n * const base = mixinFormAssociated(mixinElementInternals(LitElement));\n *\n * class MyCheckbox extends base {\n * \\@property()\n * value = 'on';\n *\n * \\@property({type: Boolean})\n * checked = false;\n *\n * override [getFormValue]() {\n * return this.checked ? this.value : null;\n * }\n *\n * override [getFormState]() {\n * return String(this.checked);\n * }\n *\n * override formResetCallback() {\n * const defaultValue = this.hasAttribute('checked');\n * this.checked = defaultValue;\n * }\n *\n * override formStateRestoreCallback(state: string) {\n * this.checked = Boolean(state);\n * }\n * }\n * ```\n *\n * IMPORTANT: Requires declares for lit-analyzer\n * @example\n * ```ts\n * const base = mixinFormAssociated(mixinElementInternals(LitElement));\n * class MyControl extends base {\n * // Writable mixin properties for lit-html binding, needed for lit-analyzer\n * declare disabled: boolean;\n * declare name: string;\n * }\n * ```\n *\n * @param base The class to mix functionality into. The base class must use\n * `mixinElementInternals()`.\n * @return The provided class with `FormAssociated` mixed in.\n */\nexport function mixinFormAssociated<\n T extends MixinBase<LitElement & WithElementInternals>,\n>(base: T): MixinReturn<T & FormAssociatedConstructor, FormAssociated> {\n abstract class FormAssociatedElement extends base implements FormAssociated {\n /** @nocollapse */\n static readonly formAssociated = true;\n\n get form() {\n return this[internals].form;\n }\n\n get labels() {\n return this[internals].labels;\n }\n\n // name attribute must be set synchronously\n @property({reflect: true})\n get name() {\n return this.getAttribute('name') ?? '';\n }\n set name(name: string) {\n const prev = this.name;\n // Setting name to null or empty string does not remove the attribute.\n this.setAttribute('name', name);\n // Explicit requestUpdate needed for Lit 2.0\n this.requestUpdate('name', prev);\n }\n\n // disabled attribute must be set synchronously\n @property({type: Boolean, reflect: true})\n get disabled() {\n return this.hasAttribute('disabled');\n }\n set disabled(disabled: boolean) {\n const prev = this.disabled;\n this.toggleAttribute('disabled', disabled);\n // Explicit requestUpdate needed for Lit 2.0\n this.requestUpdate('disabled', prev);\n }\n\n override requestUpdate(\n name?: PropertyKey,\n oldValue?: unknown,\n options?: PropertyDeclaration,\n ) {\n super.requestUpdate(name, oldValue, options);\n // If any properties change, update the form value, which may have changed\n // as well.\n // Update the form value synchronously in `requestUpdate()` rather than\n // `update()` or `updated()`, which are async. This is necessary to ensure\n // that form data is updated in time for synchronous event listeners.\n this[internals].setFormValue(this[getFormValue](), this[getFormState]());\n }\n\n [getFormValue](): FormValue | null {\n // Closure does not allow abstract symbol members, so a default\n // implementation is needed.\n throw new Error('Implement [getFormValue]');\n }\n\n [getFormState](): FormValue | null {\n return this[getFormValue]();\n }\n\n formDisabledCallback(disabled: boolean) {\n this.disabled = disabled;\n }\n\n abstract formResetCallback(): void;\n\n abstract formStateRestoreCallback(\n state: FormRestoreState | null,\n reason: FormRestoreReason,\n ): void;\n }\n\n return FormAssociatedElement;\n}\n\n/**\n * A value that can be provided for form submission and state.\n */\nexport type FormValue = File | string | FormData;\n\n/**\n * A value to be restored for a component's form value. If a component's form\n * state is a `FormData` object, its entry list of name and values will be\n * provided.\n */\nexport type FormRestoreState =\n | File\n | string\n | Array<[string, FormDataEntryValue]>;\n\n/**\n * The reason a form component is being restored for, either `'restore'` for\n * browser restoration or `'autocomplete'` for restoring user values.\n */\nexport type FormRestoreReason = 'restore' | 'autocomplete';\n"]}
|
|
1
|
+
{"version":3,"file":"form-associated.js","sourceRoot":"","sources":["form-associated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;AAGH,OAAO,EAAC,QAAQ,EAAC,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAAC,SAAS,EAAuB,MAAM,wBAAwB,CAAC;AA0GvE;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAEnD;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AACH,MAAM,UAAU,mBAAmB,CAEjC,IAAO;IACP,MAAe,qBAAsB,SAAQ,IAAI;QAI/C,IAAI,IAAI;YACN,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC;QAC9B,CAAC;QAED,IAAI,MAAM;YACR,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;QAChC,CAAC;QAED,4EAA4E;QAC5E,uEAAuE;QACvE,EAAE;QACF,wEAAwE;QACxE,2EAA2E;QAC3E,0DAA0D;QAE1D,IAAI,IAAI;YACN,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;QACD,IAAI,IAAI,CAAC,IAAY;YACnB,qEAAqE;YACrE,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAChC,0EAA0E;YAC1E,mCAAmC;QACrC,CAAC;QAGD,IAAI,QAAQ;YACV,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,QAAQ,CAAC,QAAiB;YAC5B,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC3C,0EAA0E;YAC1E,mCAAmC;QACrC,CAAC;QAEQ,wBAAwB,CAC/B,IAAY,EACZ,GAAkB,EAClB,KAAoB;YAEpB,kEAAkE;YAClE,iCAAiC;YACjC,sEAAsE;YACtE,0EAA0E;YAC1E,uEAAuE;YACvE,0EAA0E;YAC1E,wEAAwE;YACxE,0CAA0C;YAC1C,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,UAAU,EAAE;gBAC1C,uEAAuE;gBACvE,MAAM,QAAQ,GAAG,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;gBAC1D,mDAAmD;gBACnD,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBACnC,OAAO;aACR;YAED,KAAK,CAAC,wBAAwB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC;QAEQ,aAAa,CACpB,IAAkB,EAClB,QAAkB,EAClB,OAA6B;YAE7B,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC7C,0EAA0E;YAC1E,WAAW;YACX,uEAAuE;YACvE,0EAA0E;YAC1E,qEAAqE;YACrE,IAAI,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,CAAC,YAAY,CAAC;YACZ,+DAA+D;YAC/D,4BAA4B;YAC5B,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;QAED,CAAC,YAAY,CAAC;YACZ,OAAO,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,CAAC;QAED,oBAAoB,CAAC,QAAiB;YACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC3B,CAAC;;IAxFD,kBAAkB;IACF,oCAAc,GAAG,IAAI,CAAC;IAiBtC;QADC,QAAQ,CAAC,EAAC,UAAU,EAAE,IAAI,EAAC,CAAC;qDAG5B;IASD;QADC,QAAQ,CAAC,EAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAC,CAAC;yDAG3C;IAmEH,OAAO,qBAAqB,CAAC;AAC/B,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {LitElement, PropertyDeclaration} from 'lit';\nimport {property} from 'lit/decorators.js';\n\nimport {internals, WithElementInternals} from './element-internals.js';\nimport {MixinBase, MixinReturn} from './mixin.js';\n\n/**\n * A form-associated element.\n *\n * IMPORTANT: Requires declares for lit-analyzer\n * @example\n * ```ts\n * const base = mixinFormAssociated(mixinElementInternals(LitElement));\n * class MyControl extends base {\n * // Writable mixin properties for lit-html binding, needed for lit-analyzer\n * declare disabled: boolean;\n * declare name: string;\n * }\n * ```\n */\nexport interface FormAssociated {\n /**\n * The associated form element with which this element's value will submit.\n */\n readonly form: HTMLFormElement | null;\n\n /**\n * The labels this element is associated with.\n */\n readonly labels: NodeList;\n\n /**\n * The HTML name to use in form submission.\n */\n name: string;\n\n /**\n * Whether or not the element is disabled.\n */\n disabled: boolean;\n\n /**\n * Gets the current form value of a component.\n *\n * @return The current form value.\n */\n [getFormValue](): FormValue | null;\n\n /**\n * Gets the current form state of a component. Defaults to the component's\n * `[formValue]`.\n *\n * Use this when the state of an element is different from its value, such as\n * checkboxes (internal boolean state and a user string value).\n *\n * @return The current form state, defaults to the form value.\n */\n [getFormState](): FormValue | null;\n\n /**\n * A callback for when a form component should be disabled or enabled. This\n * can be called in a variety of situations, such as disabled `<fieldset>`s.\n *\n * @param disabled Whether or not the form control should be disabled.\n */\n formDisabledCallback(disabled: boolean): void;\n\n /**\n * A callback for when the form requests to reset its value. Typically, the\n * default value that is reset is represented in the attribute of an element.\n *\n * This means the attribute used for the value should not update as the value\n * changes. For example, a checkbox should not change its default `checked`\n * attribute when selected. Ensure form values do not reflect.\n */\n formResetCallback(): void;\n\n /**\n * A callback for when the form restores the state of a component. For\n * example, when a page is reloaded or forms are autofilled.\n *\n * @param state The state to restore, or null to reset the form control's\n * value.\n * @param reason The reason state was restored, either `'restore'` or\n * `'autocomplete'`.\n */\n formStateRestoreCallback(\n state: FormRestoreState | null,\n reason: FormRestoreReason,\n ): void;\n\n /**\n * An optional callback for when the associated form changes.\n *\n * @param form The new associated form, or `null` if there is none.\n */\n formAssociatedCallback?(form: HTMLFormElement | null): void;\n}\n\n/**\n * The constructor of a `FormAssociated` element.\n */\nexport interface FormAssociatedConstructor {\n /**\n * Indicates that an element is participating in form association.\n */\n readonly formAssociated: true;\n}\n\n/**\n * A symbol property to retrieve the form value for an element.\n */\nexport const getFormValue = Symbol('getFormValue');\n\n/**\n * A symbol property to retrieve the form state for an element.\n */\nexport const getFormState = Symbol('getFormState');\n\n/**\n * Mixes in form-associated behavior for a class. This allows an element to add\n * values to `<form>` elements.\n *\n * Implementing classes should provide a `[formValue]` to return the current\n * value of the element, as well as reset and restore callbacks.\n *\n * @example\n * ```ts\n * const base = mixinFormAssociated(mixinElementInternals(LitElement));\n *\n * class MyControl extends base {\n * \\@property()\n * value = '';\n *\n * override [getFormValue]() {\n * return this.value;\n * }\n *\n * override formResetCallback() {\n * const defaultValue = this.getAttribute('value');\n * this.value = defaultValue;\n * }\n *\n * override formStateRestoreCallback(state: string) {\n * this.value = state;\n * }\n * }\n * ```\n *\n * Elements may optionally provide a `[formState]` if their values do not\n * represent the state of the component.\n *\n * @example\n * ```ts\n * const base = mixinFormAssociated(mixinElementInternals(LitElement));\n *\n * class MyCheckbox extends base {\n * \\@property()\n * value = 'on';\n *\n * \\@property({type: Boolean})\n * checked = false;\n *\n * override [getFormValue]() {\n * return this.checked ? this.value : null;\n * }\n *\n * override [getFormState]() {\n * return String(this.checked);\n * }\n *\n * override formResetCallback() {\n * const defaultValue = this.hasAttribute('checked');\n * this.checked = defaultValue;\n * }\n *\n * override formStateRestoreCallback(state: string) {\n * this.checked = Boolean(state);\n * }\n * }\n * ```\n *\n * IMPORTANT: Requires declares for lit-analyzer\n * @example\n * ```ts\n * const base = mixinFormAssociated(mixinElementInternals(LitElement));\n * class MyControl extends base {\n * // Writable mixin properties for lit-html binding, needed for lit-analyzer\n * declare disabled: boolean;\n * declare name: string;\n * }\n * ```\n *\n * @param base The class to mix functionality into. The base class must use\n * `mixinElementInternals()`.\n * @return The provided class with `FormAssociated` mixed in.\n */\nexport function mixinFormAssociated<\n T extends MixinBase<LitElement & WithElementInternals>,\n>(base: T): MixinReturn<T & FormAssociatedConstructor, FormAssociated> {\n abstract class FormAssociatedElement extends base implements FormAssociated {\n /** @nocollapse */\n static readonly formAssociated = true;\n\n get form() {\n return this[internals].form;\n }\n\n get labels() {\n return this[internals].labels;\n }\n\n // Use @property for the `name` and `disabled` properties to add them to the\n // `observedAttributes` array and trigger `attributeChangedCallback()`.\n //\n // We don't use Lit's default getter/setter (`noAccessor: true`) because\n // the attributes need to be updated synchronously to work with synchronous\n // form APIs, and Lit updates attributes async by default.\n @property({noAccessor: true})\n get name() {\n return this.getAttribute('name') ?? '';\n }\n set name(name: string) {\n // Note: setting name to null or empty does not remove the attribute.\n this.setAttribute('name', name);\n // We don't need to call `requestUpdate()` since it's called synchronously\n // in `attributeChangedCallback()`.\n }\n\n @property({type: Boolean, noAccessor: true})\n get disabled() {\n return this.hasAttribute('disabled');\n }\n set disabled(disabled: boolean) {\n this.toggleAttribute('disabled', disabled);\n // We don't need to call `requestUpdate()` since it's called synchronously\n // in `attributeChangedCallback()`.\n }\n\n override attributeChangedCallback(\n name: string,\n old: string | null,\n value: string | null,\n ) {\n // Manually `requestUpdate()` for `name` and `disabled` when their\n // attribute or property changes.\n // The properties update their attributes, so this callback is invoked\n // immediately when the properties are set. We call `requestUpdate()` here\n // instead of letting Lit set the properties from the attribute change.\n // That would cause the properties to re-set the attribute and invoke this\n // callback again in a loop. This leads to stale state when Lit tries to\n // determine if a property changed or not.\n if (name === 'name' || name === 'disabled') {\n // Disabled's value is only false if the attribute is missing and null.\n const oldValue = name === 'disabled' ? old !== null : old;\n // Trigger a lit update when the attribute changes.\n this.requestUpdate(name, oldValue);\n return;\n }\n\n super.attributeChangedCallback(name, old, value);\n }\n\n override requestUpdate(\n name?: PropertyKey,\n oldValue?: unknown,\n options?: PropertyDeclaration,\n ) {\n super.requestUpdate(name, oldValue, options);\n // If any properties change, update the form value, which may have changed\n // as well.\n // Update the form value synchronously in `requestUpdate()` rather than\n // `update()` or `updated()`, which are async. This is necessary to ensure\n // that form data is updated in time for synchronous event listeners.\n this[internals].setFormValue(this[getFormValue](), this[getFormState]());\n }\n\n [getFormValue](): FormValue | null {\n // Closure does not allow abstract symbol members, so a default\n // implementation is needed.\n throw new Error('Implement [getFormValue]');\n }\n\n [getFormState](): FormValue | null {\n return this[getFormValue]();\n }\n\n formDisabledCallback(disabled: boolean) {\n this.disabled = disabled;\n }\n\n abstract formResetCallback(): void;\n\n abstract formStateRestoreCallback(\n state: FormRestoreState | null,\n reason: FormRestoreReason,\n ): void;\n }\n\n return FormAssociatedElement;\n}\n\n/**\n * A value that can be provided for form submission and state.\n */\nexport type FormValue = File | string | FormData;\n\n/**\n * A value to be restored for a component's form value. If a component's form\n * state is a `FormData` object, its entry list of name and values will be\n * provided.\n */\nexport type FormRestoreState =\n | File\n | string\n | Array<[string, FormDataEntryValue]>;\n\n/**\n * The reason a form component is being restored for, either `'restore'` for\n * browser restoration or `'autocomplete'` for restoring user values.\n */\nexport type FormRestoreReason = 'restore' | 'autocomplete';\n"]}
|
|
@@ -74,14 +74,22 @@ export class TextFieldValidator extends Validator {
|
|
|
74
74
|
// Use -1 to represent no minlength and maxlength, which is what the
|
|
75
75
|
// platform input returns. However, it will throw an error if you try to
|
|
76
76
|
// manually set it to -1.
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
//
|
|
78
|
+
// While the type is `number`, it may actually be `null` at runtime.
|
|
79
|
+
// `null > -1` is true since `null` coerces to `0`, so we default null and
|
|
80
|
+
// undefined to -1.
|
|
81
|
+
//
|
|
82
|
+
// We set attributes instead of properties since setting a property may
|
|
83
|
+
// throw an out of bounds error in relation to the other property.
|
|
84
|
+
// Attributes will not throw errors while the state is updating.
|
|
85
|
+
if ((state.minLength ?? -1) > -1) {
|
|
86
|
+
inputOrTextArea.setAttribute('minlength', String(state.minLength));
|
|
79
87
|
}
|
|
80
88
|
else {
|
|
81
89
|
inputOrTextArea.removeAttribute('minlength');
|
|
82
90
|
}
|
|
83
|
-
if (state.maxLength > -1) {
|
|
84
|
-
inputOrTextArea.
|
|
91
|
+
if ((state.maxLength ?? -1) > -1) {
|
|
92
|
+
inputOrTextArea.setAttribute('maxlength', String(state.maxLength));
|
|
85
93
|
}
|
|
86
94
|
else {
|
|
87
95
|
inputOrTextArea.removeAttribute('maxlength');
|