@matter-server/dashboard 0.7.0-alpha.0-20260512-b404bea → 0.7.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.
Files changed (171) hide show
  1. package/dist/esm/client/client-context.d.ts +3 -0
  2. package/dist/esm/client/client-context.d.ts.map +1 -1
  3. package/dist/esm/client/client-context.js +3 -1
  4. package/dist/esm/client/client-context.js.map +1 -1
  5. package/dist/esm/client/models/descriptions.js +45 -45
  6. package/dist/esm/components/avsum-ptz-strip.d.ts +41 -0
  7. package/dist/esm/components/avsum-ptz-strip.d.ts.map +1 -0
  8. package/dist/esm/components/avsum-ptz-strip.js +379 -0
  9. package/dist/esm/components/avsum-ptz-strip.js.map +6 -0
  10. package/dist/esm/components/dialogs/binding/node-binding-dialog.js +1 -1
  11. package/dist/esm/components/dialogs/binding/show-node-binding-dialog.d.ts +2 -2
  12. package/dist/esm/components/dialogs/binding/show-node-binding-dialog.d.ts.map +1 -1
  13. package/dist/esm/components/dialogs/binding/show-node-binding-dialog.js +1 -2
  14. package/dist/esm/components/dialogs/binding/show-node-binding-dialog.js.map +1 -1
  15. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-dialog.d.ts +1 -0
  16. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-dialog.d.ts.map +1 -1
  17. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-dialog.js +12 -3
  18. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-dialog.js.map +1 -1
  19. package/dist/esm/components/dialogs/commission-node-dialog/show-commission-node-dialog.d.ts +1 -2
  20. package/dist/esm/components/dialogs/commission-node-dialog/show-commission-node-dialog.d.ts.map +1 -1
  21. package/dist/esm/components/dialogs/commission-node-dialog/show-commission-node-dialog.js +1 -2
  22. package/dist/esm/components/dialogs/commission-node-dialog/show-commission-node-dialog.js.map +1 -1
  23. package/dist/esm/components/dialogs/settings/log-level-dialog.d.ts +0 -2
  24. package/dist/esm/components/dialogs/settings/log-level-dialog.d.ts.map +1 -1
  25. package/dist/esm/components/dialogs/settings/log-level-dialog.js +2 -8
  26. package/dist/esm/components/dialogs/settings/log-level-dialog.js.map +1 -1
  27. package/dist/esm/components/dialogs/settings/log-level-section.d.ts +2 -1
  28. package/dist/esm/components/dialogs/settings/log-level-section.d.ts.map +1 -1
  29. package/dist/esm/components/dialogs/settings/log-level-section.js +10 -2
  30. package/dist/esm/components/dialogs/settings/log-level-section.js.map +1 -1
  31. package/dist/esm/components/dialogs/settings/settings-dialog.d.ts +1 -0
  32. package/dist/esm/components/dialogs/settings/settings-dialog.d.ts.map +1 -1
  33. package/dist/esm/components/dialogs/settings/settings-dialog.js +8 -2
  34. package/dist/esm/components/dialogs/settings/settings-dialog.js.map +1 -1
  35. package/dist/esm/components/dialogs/settings/show-log-level-dialog.d.ts +1 -2
  36. package/dist/esm/components/dialogs/settings/show-log-level-dialog.d.ts.map +1 -1
  37. package/dist/esm/components/dialogs/settings/show-log-level-dialog.js +1 -2
  38. package/dist/esm/components/dialogs/settings/show-log-level-dialog.js.map +1 -1
  39. package/dist/esm/components/dialogs/settings/show-settings-dialog.d.ts +1 -2
  40. package/dist/esm/components/dialogs/settings/show-settings-dialog.d.ts.map +1 -1
  41. package/dist/esm/components/dialogs/settings/show-settings-dialog.js +1 -2
  42. package/dist/esm/components/dialogs/settings/show-settings-dialog.js.map +1 -1
  43. package/dist/esm/components/webrtc-stream-view.d.ts +97 -0
  44. package/dist/esm/components/webrtc-stream-view.d.ts.map +1 -0
  45. package/dist/esm/components/webrtc-stream-view.js +878 -0
  46. package/dist/esm/components/webrtc-stream-view.js.map +6 -0
  47. package/dist/esm/pages/camera-overlay.d.ts +63 -0
  48. package/dist/esm/pages/camera-overlay.d.ts.map +1 -0
  49. package/dist/esm/pages/camera-overlay.js +546 -0
  50. package/dist/esm/pages/camera-overlay.js.map +6 -0
  51. package/dist/esm/pages/cluster-commands/base-cluster-commands.d.ts +1 -0
  52. package/dist/esm/pages/cluster-commands/base-cluster-commands.d.ts.map +1 -1
  53. package/dist/esm/pages/cluster-commands/base-cluster-commands.js +11 -1
  54. package/dist/esm/pages/cluster-commands/base-cluster-commands.js.map +1 -1
  55. package/dist/esm/pages/cluster-commands/clusters/avsum-commands.d.ts +38 -0
  56. package/dist/esm/pages/cluster-commands/clusters/avsum-commands.d.ts.map +1 -0
  57. package/dist/esm/pages/cluster-commands/clusters/avsum-commands.js +534 -0
  58. package/dist/esm/pages/cluster-commands/clusters/avsum-commands.js.map +6 -0
  59. package/dist/esm/pages/cluster-commands/clusters/chime-commands.d.ts +32 -0
  60. package/dist/esm/pages/cluster-commands/clusters/chime-commands.d.ts.map +1 -0
  61. package/dist/esm/pages/cluster-commands/clusters/chime-commands.js +261 -0
  62. package/dist/esm/pages/cluster-commands/clusters/chime-commands.js.map +6 -0
  63. package/dist/esm/pages/cluster-commands/index.d.ts +2 -0
  64. package/dist/esm/pages/cluster-commands/index.d.ts.map +1 -1
  65. package/dist/esm/pages/cluster-commands/index.js +2 -0
  66. package/dist/esm/pages/cluster-commands/index.js.map +1 -1
  67. package/dist/esm/pages/components/header.d.ts +1 -0
  68. package/dist/esm/pages/components/header.d.ts.map +1 -1
  69. package/dist/esm/pages/components/header.js +10 -3
  70. package/dist/esm/pages/components/header.js.map +1 -1
  71. package/dist/esm/pages/components/node-details.d.ts +3 -0
  72. package/dist/esm/pages/components/node-details.d.ts.map +1 -1
  73. package/dist/esm/pages/components/node-details.js +34 -4
  74. package/dist/esm/pages/components/node-details.js.map +1 -1
  75. package/dist/esm/pages/components/server-details.d.ts +1 -0
  76. package/dist/esm/pages/components/server-details.d.ts.map +1 -1
  77. package/dist/esm/pages/components/server-details.js +11 -2
  78. package/dist/esm/pages/components/server-details.js.map +1 -1
  79. package/dist/esm/pages/matter-cluster-view.d.ts +1 -0
  80. package/dist/esm/pages/matter-cluster-view.d.ts.map +1 -1
  81. package/dist/esm/pages/matter-cluster-view.js +15 -8
  82. package/dist/esm/pages/matter-cluster-view.js.map +1 -1
  83. package/dist/esm/pages/matter-dashboard-app.d.ts +3 -1
  84. package/dist/esm/pages/matter-dashboard-app.d.ts.map +1 -1
  85. package/dist/esm/pages/matter-dashboard-app.js +12 -16
  86. package/dist/esm/pages/matter-dashboard-app.js.map +1 -1
  87. package/dist/esm/pages/matter-endpoint-view.d.ts +2 -1
  88. package/dist/esm/pages/matter-endpoint-view.d.ts.map +1 -1
  89. package/dist/esm/pages/matter-endpoint-view.js +17 -3
  90. package/dist/esm/pages/matter-endpoint-view.js.map +1 -1
  91. package/dist/esm/pages/matter-network-view.d.ts.map +1 -1
  92. package/dist/esm/pages/matter-network-view.js +5 -1
  93. package/dist/esm/pages/matter-network-view.js.map +1 -1
  94. package/dist/esm/pages/matter-node-view.d.ts +1 -0
  95. package/dist/esm/pages/matter-node-view.d.ts.map +1 -1
  96. package/dist/esm/pages/matter-node-view.js +15 -7
  97. package/dist/esm/pages/matter-node-view.js.map +1 -1
  98. package/dist/esm/pages/matter-server-view.d.ts +1 -0
  99. package/dist/esm/pages/matter-server-view.d.ts.map +1 -1
  100. package/dist/esm/pages/matter-server-view.js +13 -2
  101. package/dist/esm/pages/matter-server-view.js.map +1 -1
  102. package/dist/esm/pages/network/network-details.d.ts.map +1 -1
  103. package/dist/esm/pages/network/network-details.js +0 -3
  104. package/dist/esm/pages/network/network-details.js.map +1 -1
  105. package/dist/esm/pages/network/update-connections-dialog.d.ts +1 -0
  106. package/dist/esm/pages/network/update-connections-dialog.d.ts.map +1 -1
  107. package/dist/esm/pages/network/update-connections-dialog.js +7 -1
  108. package/dist/esm/pages/network/update-connections-dialog.js.map +1 -1
  109. package/dist/esm/util/attribute-shapes.d.ts +11 -0
  110. package/dist/esm/util/attribute-shapes.d.ts.map +1 -0
  111. package/dist/esm/util/attribute-shapes.js +37 -0
  112. package/dist/esm/util/attribute-shapes.js.map +6 -0
  113. package/dist/esm/util/avsum.d.ts +69 -0
  114. package/dist/esm/util/avsum.d.ts.map +1 -0
  115. package/dist/esm/util/avsum.js +150 -0
  116. package/dist/esm/util/avsum.js.map +6 -0
  117. package/dist/esm/util/chime.d.ts +20 -0
  118. package/dist/esm/util/chime.d.ts.map +1 -0
  119. package/dist/esm/util/chime.js +60 -0
  120. package/dist/esm/util/chime.js.map +6 -0
  121. package/dist/web/js/{attribute-write-dialog-DzMWN_T3.js → attribute-write-dialog-DLRP2hlx.js} +1 -1
  122. package/dist/web/js/{command-invoke-dialog-BhAOXzjX.js → command-invoke-dialog-BcCfT1sn.js} +1 -1
  123. package/dist/web/js/{commission-node-dialog-DF87YIqR.js → commission-node-dialog-BfldkUxD.js} +16 -8
  124. package/dist/web/js/{commission-node-existing-DLcWvJTL.js → commission-node-existing-omW-An0J.js} +2 -2
  125. package/dist/web/js/{commission-node-thread-m2fqED-2.js → commission-node-thread-BZpJIxlI.js} +2 -2
  126. package/dist/web/js/{commission-node-wifi-PCsot-CX.js → commission-node-wifi-HA2pYTW2.js} +2 -2
  127. package/dist/web/js/{dialog-box-DiqYXM8c.js → dialog-box-BjDID9j4.js} +1 -1
  128. package/dist/web/js/{fire_event-BnpND_gK.js → fire_event-CChLgSiV.js} +1 -1
  129. package/dist/web/js/main.js +39 -1
  130. package/dist/web/js/{matter-dashboard-app-BZOhBELR.js → matter-dashboard-app-XUWBcruM.js} +8265 -2035
  131. package/dist/web/js/{node-binding-dialog-B3lVN-FU.js → node-binding-dialog-_yHtf7WP.js} +2 -2
  132. package/dist/web/js/settings-dialog-BFPfOSOI.js +607 -0
  133. package/package.json +4 -4
  134. package/src/client/client-context.ts +1 -0
  135. package/src/client/models/descriptions.ts +45 -45
  136. package/src/components/avsum-ptz-strip.ts +388 -0
  137. package/src/components/dialogs/binding/node-binding-dialog.ts +1 -1
  138. package/src/components/dialogs/binding/show-node-binding-dialog.ts +2 -3
  139. package/src/components/dialogs/commission-node-dialog/commission-node-dialog.ts +10 -4
  140. package/src/components/dialogs/commission-node-dialog/show-commission-node-dialog.ts +1 -4
  141. package/src/components/dialogs/settings/log-level-dialog.ts +2 -9
  142. package/src/components/dialogs/settings/log-level-section.ts +11 -4
  143. package/src/components/dialogs/settings/settings-dialog.ts +8 -3
  144. package/src/components/dialogs/settings/show-log-level-dialog.ts +1 -4
  145. package/src/components/dialogs/settings/show-settings-dialog.ts +1 -4
  146. package/src/components/webrtc-stream-view.ts +999 -0
  147. package/src/pages/camera-overlay.ts +562 -0
  148. package/src/pages/cluster-commands/base-cluster-commands.ts +7 -1
  149. package/src/pages/cluster-commands/clusters/avsum-commands.ts +562 -0
  150. package/src/pages/cluster-commands/clusters/chime-commands.ts +272 -0
  151. package/src/pages/cluster-commands/index.ts +2 -0
  152. package/src/pages/components/header.ts +7 -3
  153. package/src/pages/components/node-details.ts +33 -3
  154. package/src/pages/components/server-details.ts +8 -2
  155. package/src/pages/matter-cluster-view.ts +13 -9
  156. package/src/pages/matter-dashboard-app.ts +11 -16
  157. package/src/pages/matter-endpoint-view.ts +13 -5
  158. package/src/pages/matter-network-view.ts +3 -1
  159. package/src/pages/matter-node-view.ts +9 -7
  160. package/src/pages/matter-server-view.ts +7 -2
  161. package/src/pages/network/network-details.ts +0 -3
  162. package/src/pages/network/update-connections-dialog.ts +6 -1
  163. package/src/util/attribute-shapes.ts +34 -0
  164. package/src/util/avsum.ts +212 -0
  165. package/src/util/chime.ts +82 -0
  166. package/dist/esm/util/clone_class.d.ts +0 -7
  167. package/dist/esm/util/clone_class.d.ts.map +0 -1
  168. package/dist/esm/util/clone_class.js +0 -10
  169. package/dist/esm/util/clone_class.js.map +0 -6
  170. package/dist/web/js/settings-dialog-D75IAPKe.js +0 -4039
  171. package/src/util/clone_class.ts +0 -7
@@ -1,4039 +0,0 @@
1
- import { e, m as mixinDelegatesAria, a as mixinConstraintValidation, b as mixinFormAssociated, c as mixinElementInternals, i, _ as __decorate, n as n$1, d as e$1, f as isActivationClick, g as dispatchActivationClick, A, h as e$2, j as b, r as redispatchEvent, k as getFormValue, l as getFormState, o as createValidator, C as CheckboxValidator, p as getValidityAnchor, q as i$1, t, N as NavigableKeys, s as o, u as r, v as createAnimationSignal, L as ListController, w as getActiveItem, x as getLastActivatableItem, y as getFirstActivatableItem, z as o$1, E as EASING, V as Validator, D, B as mixinOnReportValidity, F as onReportValidity, G as u, H as i$2, I as fireAndForget, J as showAlertDialog, K as handleAsync, M as DevModeService, O as mdiWifi, P as mdiAccessPoint, Q as mdiEyeOff, R as mdiEye } from './matter-dashboard-app-BZOhBELR.js';
2
- import { p as preventDefault } from './prevent_default-D-ohDGsN.js';
3
- import './main.js';
4
-
5
- /**
6
- * @license
7
- * Copyright 2017 Google LLC
8
- * SPDX-License-Identifier: BSD-3-Clause
9
- */
10
- function n(n) {
11
- return (o, r) => {
12
- const {
13
- slot: e$1
14
- } = n ?? {},
15
- s = "slot" + (e$1 ? `[name=${e$1}]` : ":not([name])");
16
- return e(o, r, {
17
- get() {
18
- var _this$renderRoot;
19
- const t = (_this$renderRoot = this.renderRoot) === null || _this$renderRoot === void 0 ? void 0 : _this$renderRoot.querySelector(s);
20
- return (t === null || t === void 0 ? void 0 : t.assignedNodes(n)) ?? [];
21
- }
22
- });
23
- };
24
- }
25
-
26
- /**
27
- * @license
28
- * Copyright 2023 Google LLC
29
- * SPDX-License-Identifier: Apache-2.0
30
- */
31
- /**
32
- * A symbol used to access dispatch hooks on an event.
33
- */
34
- const dispatchHooks = Symbol('dispatchHooks');
35
- /**
36
- * Add a hook for an event that is called after the event is dispatched and
37
- * propagates to other event listeners.
38
- *
39
- * This is useful for behaviors that need to check if an event is canceled.
40
- *
41
- * The callback is invoked synchronously, which allows for better integration
42
- * with synchronous platform APIs (like `<form>` or `<label>` clicking).
43
- *
44
- * Note: `setupDispatchHooks()` must be called on the element before adding any
45
- * other event listeners. Call it in the constructor of an element or
46
- * controller.
47
- *
48
- * @example
49
- * ```ts
50
- * class MyControl extends LitElement {
51
- * constructor() {
52
- * super();
53
- * setupDispatchHooks(this, 'click');
54
- * this.addEventListener('click', event => {
55
- * afterDispatch(event, () => {
56
- * if (event.defaultPrevented) {
57
- * return
58
- * }
59
- *
60
- * // ... perform logic
61
- * });
62
- * });
63
- * }
64
- * }
65
- * ```
66
- *
67
- * @example
68
- * ```ts
69
- * class MyController implements ReactiveController {
70
- * constructor(host: ReactiveElement) {
71
- * // setupDispatchHooks() may be called multiple times for the same
72
- * // element and events, making it safe for multiple controllers to use it.
73
- * setupDispatchHooks(host, 'click');
74
- * host.addEventListener('click', event => {
75
- * afterDispatch(event, () => {
76
- * if (event.defaultPrevented) {
77
- * return;
78
- * }
79
- *
80
- * // ... perform logic
81
- * });
82
- * });
83
- * }
84
- * }
85
- * ```
86
- *
87
- * @param event The event to add a hook to.
88
- * @param callback A hook that is called after the event finishes dispatching.
89
- */
90
- function afterDispatch(event, callback) {
91
- const hooks = event[dispatchHooks];
92
- if (!hooks) {
93
- throw new Error(`'${event.type}' event needs setupDispatchHooks().`);
94
- }
95
- hooks.addEventListener('after', callback);
96
- }
97
- /**
98
- * A lookup map of elements and event types that have a dispatch hook listener
99
- * set up. Used to ensure we don't set up multiple hook listeners on the same
100
- * element for the same event.
101
- */
102
- const ELEMENT_DISPATCH_HOOK_TYPES = new WeakMap();
103
- /**
104
- * Sets up an element to add dispatch hooks to given event types. This must be
105
- * called before adding any event listeners that need to use dispatch hooks
106
- * like `afterDispatch()`.
107
- *
108
- * This function is safe to call multiple times with the same element or event
109
- * types. Call it in the constructor of elements, mixins, and controllers to
110
- * ensure it is set up before external listeners.
111
- *
112
- * @example
113
- * ```ts
114
- * class MyControl extends LitElement {
115
- * constructor() {
116
- * super();
117
- * setupDispatchHooks(this, 'click');
118
- * this.addEventListener('click', this.listenerUsingAfterDispatch);
119
- * }
120
- * }
121
- * ```
122
- *
123
- * @param element The element to set up event dispatch hooks for.
124
- * @param eventTypes The event types to add dispatch hooks to.
125
- */
126
- function setupDispatchHooks(element, ...eventTypes) {
127
- let typesAlreadySetUp = ELEMENT_DISPATCH_HOOK_TYPES.get(element);
128
- if (!typesAlreadySetUp) {
129
- typesAlreadySetUp = new Set();
130
- ELEMENT_DISPATCH_HOOK_TYPES.set(element, typesAlreadySetUp);
131
- }
132
- for (const eventType of eventTypes) {
133
- // Don't register multiple dispatch hook listeners. A second registration
134
- // would lead to the second listener re-dispatching a re-dispatched event,
135
- // which can cause an infinite loop inside the other one.
136
- if (typesAlreadySetUp.has(eventType)) {
137
- continue;
138
- }
139
- // When we re-dispatch the event, it's going to immediately trigger this
140
- // listener again. Use a flag to ignore it.
141
- let isRedispatching = false;
142
- element.addEventListener(eventType, event => {
143
- if (isRedispatching) {
144
- return;
145
- }
146
- // Do not let the event propagate to any other listener (not just
147
- // bubbling listeners with `stopPropagation()`).
148
- event.stopImmediatePropagation();
149
- // Make a copy.
150
- const eventCopy = Reflect.construct(event.constructor, [event.type, event]);
151
- // Add hooks onto the event.
152
- const hooks = new EventTarget();
153
- eventCopy[dispatchHooks] = hooks;
154
- // Re-dispatch the event. We can't reuse `redispatchEvent()` since we
155
- // need to add the hooks to the copy before it's dispatched.
156
- isRedispatching = true;
157
- const dispatched = element.dispatchEvent(eventCopy);
158
- isRedispatching = false;
159
- if (!dispatched) {
160
- event.preventDefault();
161
- }
162
- // Synchronously call afterDispatch() hooks.
163
- hooks.dispatchEvent(new Event('after'));
164
- }, {
165
- // Ensure this listener runs before other listeners.
166
- // `setupDispatchHooks()` should be called in constructors to also
167
- // ensure they run before any other externally-added capture listeners.
168
- capture: true
169
- });
170
- typesAlreadySetUp.add(eventType);
171
- }
172
- }
173
-
174
- /**
175
- * @license
176
- * Copyright 2021 Google LLC
177
- * SPDX-License-Identifier: Apache-2.0
178
- */
179
- // Separate variable needed for closure.
180
- const switchBaseClass = mixinDelegatesAria(mixinConstraintValidation(mixinFormAssociated(mixinElementInternals(i))));
181
- /**
182
- * @fires input {InputEvent} Fired whenever `selected` changes due to user
183
- * interaction (bubbles and composed).
184
- * @fires change {Event} Fired whenever `selected` changes due to user
185
- * interaction (bubbles).
186
- */
187
- class Switch extends switchBaseClass {
188
- constructor() {
189
- super();
190
- /**
191
- * Puts the switch in the selected state and sets the form submission value to
192
- * the `value` property.
193
- */
194
- this.selected = false;
195
- /**
196
- * Shows both the selected and deselected icons.
197
- */
198
- this.icons = false;
199
- /**
200
- * Shows only the selected icon, and not the deselected icon. If `true`,
201
- * overrides the behavior of the `icons` property.
202
- */
203
- this.showOnlySelectedIcon = false;
204
- /**
205
- * When true, require the switch to be selected when participating in
206
- * form submission.
207
- *
208
- * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#validation
209
- */
210
- this.required = false;
211
- /**
212
- * The value associated with this switch on form submission. `null` is
213
- * submitted when `selected` is `false`.
214
- */
215
- this.value = 'on';
216
- // This click listener does not currently need dispatch hooks since it does
217
- // not check `event.defaultPrevented`.
218
- this.addEventListener('click', event => {
219
- if (!isActivationClick(event) || !this.input) {
220
- return;
221
- }
222
- this.focus();
223
- dispatchActivationClick(this.input);
224
- });
225
- // Add the aria keyboard interaction pattern for switch and the Enter key.
226
- // See https://www.w3.org/WAI/ARIA/apg/patterns/switch/.
227
- setupDispatchHooks(this, 'keydown');
228
- this.addEventListener('keydown', event => {
229
- afterDispatch(event, () => {
230
- const ignoreEvent = event.defaultPrevented || event.key !== 'Enter';
231
- if (ignoreEvent || this.disabled || !this.input) {
232
- return;
233
- }
234
- this.input.click();
235
- });
236
- });
237
- }
238
- render() {
239
- return b`
240
- <div class="switch ${e$2(this.getRenderClasses())}">
241
- <input
242
- id="switch"
243
- class="touch"
244
- type="checkbox"
245
- role="switch"
246
- aria-label=${this.ariaLabel || A}
247
- ?checked=${this.selected}
248
- ?disabled=${this.disabled}
249
- ?required=${this.required}
250
- @input=${this.handleInput}
251
- @change=${this.handleChange} />
252
-
253
- <md-focus-ring part="focus-ring" for="switch"></md-focus-ring>
254
- <span class="track"> ${this.renderHandle()} </span>
255
- </div>
256
- `;
257
- }
258
- getRenderClasses() {
259
- return {
260
- 'selected': this.selected,
261
- 'unselected': !this.selected,
262
- 'disabled': this.disabled
263
- };
264
- }
265
- renderHandle() {
266
- const classes = {
267
- 'with-icon': this.showOnlySelectedIcon ? this.selected : this.icons
268
- };
269
- return b`
270
- ${this.renderTouchTarget()}
271
- <span class="handle-container">
272
- <md-ripple for="switch" ?disabled="${this.disabled}"></md-ripple>
273
- <span class="handle ${e$2(classes)}">
274
- ${this.shouldShowIcons() ? this.renderIcons() : b``}
275
- </span>
276
- </span>
277
- `;
278
- }
279
- renderIcons() {
280
- return b`
281
- <div class="icons">
282
- ${this.renderOnIcon()}
283
- ${this.showOnlySelectedIcon ? b`` : this.renderOffIcon()}
284
- </div>
285
- `;
286
- }
287
- /**
288
- * https://fonts.google.com/icons?selected=Material%20Symbols%20Outlined%3Acheck%3AFILL%400%3Bwght%40500%3BGRAD%400%3Bopsz%4024
289
- */
290
- renderOnIcon() {
291
- return b`
292
- <slot class="icon icon--on" name="on-icon">
293
- <svg viewBox="0 0 24 24">
294
- <path
295
- d="M9.55 18.2 3.65 12.3 5.275 10.675 9.55 14.95 18.725 5.775 20.35 7.4Z" />
296
- </svg>
297
- </slot>
298
- `;
299
- }
300
- /**
301
- * https://fonts.google.com/icons?selected=Material%20Symbols%20Outlined%3Aclose%3AFILL%400%3Bwght%40500%3BGRAD%400%3Bopsz%4024
302
- */
303
- renderOffIcon() {
304
- return b`
305
- <slot class="icon icon--off" name="off-icon">
306
- <svg viewBox="0 0 24 24">
307
- <path
308
- d="M6.4 19.2 4.8 17.6 10.4 12 4.8 6.4 6.4 4.8 12 10.4 17.6 4.8 19.2 6.4 13.6 12 19.2 17.6 17.6 19.2 12 13.6Z" />
309
- </svg>
310
- </slot>
311
- `;
312
- }
313
- renderTouchTarget() {
314
- return b`<span class="touch"></span>`;
315
- }
316
- shouldShowIcons() {
317
- return this.icons || this.showOnlySelectedIcon;
318
- }
319
- handleInput(event) {
320
- const target = event.target;
321
- this.selected = target.checked;
322
- // <input> 'input' event bubbles and is composed, don't re-dispatch it.
323
- }
324
- handleChange(event) {
325
- // <input> 'change' event is not composed, re-dispatch it.
326
- redispatchEvent(this, event);
327
- }
328
- [getFormValue]() {
329
- return this.selected ? this.value : null;
330
- }
331
- [getFormState]() {
332
- return String(this.selected);
333
- }
334
- formResetCallback() {
335
- // The selected property does not reflect, so the original attribute set by
336
- // the user is used to determine the default value.
337
- this.selected = this.hasAttribute('selected');
338
- }
339
- formStateRestoreCallback(state) {
340
- this.selected = state === 'true';
341
- }
342
- [createValidator]() {
343
- return new CheckboxValidator(() => ({
344
- checked: this.selected,
345
- required: this.required
346
- }));
347
- }
348
- [getValidityAnchor]() {
349
- return this.input;
350
- }
351
- }
352
- /** @nocollapse */
353
- Switch.shadowRootOptions = {
354
- mode: 'open',
355
- delegatesFocus: true
356
- };
357
- __decorate([n$1({
358
- type: Boolean
359
- })], Switch.prototype, "selected", void 0);
360
- __decorate([n$1({
361
- type: Boolean
362
- })], Switch.prototype, "icons", void 0);
363
- __decorate([n$1({
364
- type: Boolean,
365
- attribute: 'show-only-selected-icon'
366
- })], Switch.prototype, "showOnlySelectedIcon", void 0);
367
- __decorate([n$1({
368
- type: Boolean
369
- })], Switch.prototype, "required", void 0);
370
- __decorate([n$1()], Switch.prototype, "value", void 0);
371
- __decorate([e$1('input')], Switch.prototype, "input", void 0);
372
-
373
- /**
374
- * @license
375
- * Copyright 2024 Google LLC
376
- * SPDX-License-Identifier: Apache-2.0
377
- */
378
- // Generated stylesheet for ./switch/internal/switch-styles.css.
379
- const styles$4 = i$1`@layer styles, hcm;@layer styles{:host{display:inline-flex;outline:none;vertical-align:top;-webkit-tap-highlight-color:rgba(0,0,0,0);cursor:pointer}:host([disabled]){cursor:default}:host([touch-target=wrapper]){margin:max(0px,(48px - var(--md-switch-track-height, 32px))/2) 0px}md-focus-ring{--md-focus-ring-shape-start-start: var(--md-switch-track-shape-start-start, var(--md-switch-track-shape, var(--md-sys-shape-corner-full, 9999px)));--md-focus-ring-shape-start-end: var(--md-switch-track-shape-start-end, var(--md-switch-track-shape, var(--md-sys-shape-corner-full, 9999px)));--md-focus-ring-shape-end-end: var(--md-switch-track-shape-end-end, var(--md-switch-track-shape, var(--md-sys-shape-corner-full, 9999px)));--md-focus-ring-shape-end-start: var(--md-switch-track-shape-end-start, var(--md-switch-track-shape, var(--md-sys-shape-corner-full, 9999px)))}.switch{align-items:center;display:inline-flex;flex-shrink:0;position:relative;width:var(--md-switch-track-width, 52px);height:var(--md-switch-track-height, 32px);border-start-start-radius:var(--md-switch-track-shape-start-start, var(--md-switch-track-shape, var(--md-sys-shape-corner-full, 9999px)));border-start-end-radius:var(--md-switch-track-shape-start-end, var(--md-switch-track-shape, var(--md-sys-shape-corner-full, 9999px)));border-end-end-radius:var(--md-switch-track-shape-end-end, var(--md-switch-track-shape, var(--md-sys-shape-corner-full, 9999px)));border-end-start-radius:var(--md-switch-track-shape-end-start, var(--md-switch-track-shape, var(--md-sys-shape-corner-full, 9999px)))}input{appearance:none;height:max(100%,var(--md-switch-touch-target-size, 48px));outline:none;margin:0;position:absolute;width:max(100%,var(--md-switch-touch-target-size, 48px));z-index:1;cursor:inherit;top:50%;left:50%;transform:translate(-50%, -50%)}:host([touch-target=none]) input{display:none}}@layer styles{.track{position:absolute;width:100%;height:100%;box-sizing:border-box;border-radius:inherit;display:flex;justify-content:center;align-items:center}.track::before{content:"";display:flex;position:absolute;height:100%;width:100%;border-radius:inherit;box-sizing:border-box;transition-property:opacity,background-color;transition-timing-function:linear;transition-duration:67ms}.disabled .track{background-color:rgba(0,0,0,0);border-color:rgba(0,0,0,0)}.disabled .track::before,.disabled .track::after{transition:none;opacity:var(--md-switch-disabled-track-opacity, 0.12)}.disabled .track::before{background-clip:content-box}.selected .track::before{background-color:var(--md-switch-selected-track-color, var(--md-sys-color-primary, #6750a4))}.selected:hover .track::before{background-color:var(--md-switch-selected-hover-track-color, var(--md-sys-color-primary, #6750a4))}.selected:focus-within .track::before{background-color:var(--md-switch-selected-focus-track-color, var(--md-sys-color-primary, #6750a4))}.selected:active .track::before{background-color:var(--md-switch-selected-pressed-track-color, var(--md-sys-color-primary, #6750a4))}.selected.disabled .track{background-clip:border-box}.selected.disabled .track::before{background-color:var(--md-switch-disabled-selected-track-color, var(--md-sys-color-on-surface, #1d1b20))}.unselected .track::before{background-color:var(--md-switch-track-color, var(--md-sys-color-surface-container-highest, #e6e0e9));border-color:var(--md-switch-track-outline-color, var(--md-sys-color-outline, #79747e));border-style:solid;border-width:var(--md-switch-track-outline-width, 2px)}.unselected:hover .track::before{background-color:var(--md-switch-hover-track-color, var(--md-sys-color-surface-container-highest, #e6e0e9));border-color:var(--md-switch-hover-track-outline-color, var(--md-sys-color-outline, #79747e))}.unselected:focus-visible .track::before{background-color:var(--md-switch-focus-track-color, var(--md-sys-color-surface-container-highest, #e6e0e9));border-color:var(--md-switch-focus-track-outline-color, var(--md-sys-color-outline, #79747e))}.unselected:active .track::before{background-color:var(--md-switch-pressed-track-color, var(--md-sys-color-surface-container-highest, #e6e0e9));border-color:var(--md-switch-pressed-track-outline-color, var(--md-sys-color-outline, #79747e))}.unselected.disabled .track::before{background-color:var(--md-switch-disabled-track-color, var(--md-sys-color-surface-container-highest, #e6e0e9));border-color:var(--md-switch-disabled-track-outline-color, var(--md-sys-color-on-surface, #1d1b20))}}@layer hcm{@media(forced-colors: active){.selected .track::before{background:ButtonText;border-color:ButtonText}.disabled .track::before{border-color:GrayText;opacity:1}.disabled.selected .track::before{background:GrayText}}}@layer styles{.handle-container{display:flex;place-content:center;place-items:center;position:relative;transition:margin 300ms cubic-bezier(0.175, 0.885, 0.32, 1.275)}.selected .handle-container{margin-inline-start:calc(var(--md-switch-track-width, 52px) - var(--md-switch-track-height, 32px))}.unselected .handle-container{margin-inline-end:calc(var(--md-switch-track-width, 52px) - var(--md-switch-track-height, 32px))}.disabled .handle-container{transition:none}.handle{border-start-start-radius:var(--md-switch-handle-shape-start-start, var(--md-switch-handle-shape, var(--md-sys-shape-corner-full, 9999px)));border-start-end-radius:var(--md-switch-handle-shape-start-end, var(--md-switch-handle-shape, var(--md-sys-shape-corner-full, 9999px)));border-end-end-radius:var(--md-switch-handle-shape-end-end, var(--md-switch-handle-shape, var(--md-sys-shape-corner-full, 9999px)));border-end-start-radius:var(--md-switch-handle-shape-end-start, var(--md-switch-handle-shape, var(--md-sys-shape-corner-full, 9999px)));height:var(--md-switch-handle-height, 16px);width:var(--md-switch-handle-width, 16px);transform-origin:center;transition-property:height,width;transition-duration:250ms,250ms;transition-timing-function:cubic-bezier(0.2, 0, 0, 1),cubic-bezier(0.2, 0, 0, 1);z-index:0}.handle::before{content:"";display:flex;inset:0;position:absolute;border-radius:inherit;box-sizing:border-box;transition:background-color 67ms linear}.disabled .handle,.disabled .handle::before{transition:none}.selected .handle{height:var(--md-switch-selected-handle-height, 24px);width:var(--md-switch-selected-handle-width, 24px)}.handle.with-icon{height:var(--md-switch-with-icon-handle-height, 24px);width:var(--md-switch-with-icon-handle-width, 24px)}.selected:not(.disabled):active .handle,.unselected:not(.disabled):active .handle{height:var(--md-switch-pressed-handle-height, 28px);width:var(--md-switch-pressed-handle-width, 28px);transition-timing-function:linear;transition-duration:100ms}.selected .handle::before{background-color:var(--md-switch-selected-handle-color, var(--md-sys-color-on-primary, #fff))}.selected:hover .handle::before{background-color:var(--md-switch-selected-hover-handle-color, var(--md-sys-color-primary-container, #eaddff))}.selected:focus-within .handle::before{background-color:var(--md-switch-selected-focus-handle-color, var(--md-sys-color-primary-container, #eaddff))}.selected:active .handle::before{background-color:var(--md-switch-selected-pressed-handle-color, var(--md-sys-color-primary-container, #eaddff))}.selected.disabled .handle::before{background-color:var(--md-switch-disabled-selected-handle-color, var(--md-sys-color-surface, #fef7ff));opacity:var(--md-switch-disabled-selected-handle-opacity, 1)}.unselected .handle::before{background-color:var(--md-switch-handle-color, var(--md-sys-color-outline, #79747e))}.unselected:hover .handle::before{background-color:var(--md-switch-hover-handle-color, var(--md-sys-color-on-surface-variant, #49454f))}.unselected:focus-within .handle::before{background-color:var(--md-switch-focus-handle-color, var(--md-sys-color-on-surface-variant, #49454f))}.unselected:active .handle::before{background-color:var(--md-switch-pressed-handle-color, var(--md-sys-color-on-surface-variant, #49454f))}.unselected.disabled .handle::before{background-color:var(--md-switch-disabled-handle-color, var(--md-sys-color-on-surface, #1d1b20));opacity:var(--md-switch-disabled-handle-opacity, 0.38)}md-ripple{border-radius:var(--md-switch-state-layer-shape, var(--md-sys-shape-corner-full, 9999px));height:var(--md-switch-state-layer-size, 40px);inset:unset;width:var(--md-switch-state-layer-size, 40px)}.selected md-ripple{--md-ripple-hover-color: var(--md-switch-selected-hover-state-layer-color, var(--md-sys-color-primary, #6750a4));--md-ripple-pressed-color: var(--md-switch-selected-pressed-state-layer-color, var(--md-sys-color-primary, #6750a4));--md-ripple-hover-opacity: var(--md-switch-selected-hover-state-layer-opacity, 0.08);--md-ripple-pressed-opacity: var(--md-switch-selected-pressed-state-layer-opacity, 0.12)}.unselected md-ripple{--md-ripple-hover-color: var(--md-switch-hover-state-layer-color, var(--md-sys-color-on-surface, #1d1b20));--md-ripple-pressed-color: var(--md-switch-pressed-state-layer-color, var(--md-sys-color-on-surface, #1d1b20));--md-ripple-hover-opacity: var(--md-switch-hover-state-layer-opacity, 0.08);--md-ripple-pressed-opacity: var(--md-switch-pressed-state-layer-opacity, 0.12)}}@layer hcm{@media(forced-colors: active){.unselected .handle::before{background:ButtonText}.disabled .handle::before{opacity:1}.disabled.unselected .handle::before{background:GrayText}}}@layer styles{.icons{position:relative;height:100%;width:100%}.icon{position:absolute;inset:0;margin:auto;display:flex;align-items:center;justify-content:center;fill:currentColor;transition:fill 67ms linear,opacity 33ms linear,transform 167ms cubic-bezier(0.2, 0, 0, 1);opacity:0}.disabled .icon{transition:none}.selected .icon--on,.unselected .icon--off{opacity:1}.unselected .handle:not(.with-icon) .icon--on{transform:rotate(-45deg)}.icon--off{width:var(--md-switch-icon-size, 16px);height:var(--md-switch-icon-size, 16px);color:var(--md-switch-icon-color, var(--md-sys-color-surface-container-highest, #e6e0e9))}.unselected:hover .icon--off{color:var(--md-switch-hover-icon-color, var(--md-sys-color-surface-container-highest, #e6e0e9))}.unselected:focus-within .icon--off{color:var(--md-switch-focus-icon-color, var(--md-sys-color-surface-container-highest, #e6e0e9))}.unselected:active .icon--off{color:var(--md-switch-pressed-icon-color, var(--md-sys-color-surface-container-highest, #e6e0e9))}.unselected.disabled .icon--off{color:var(--md-switch-disabled-icon-color, var(--md-sys-color-surface-container-highest, #e6e0e9));opacity:var(--md-switch-disabled-icon-opacity, 0.38)}.icon--on{width:var(--md-switch-selected-icon-size, 16px);height:var(--md-switch-selected-icon-size, 16px);color:var(--md-switch-selected-icon-color, var(--md-sys-color-on-primary-container, #21005d))}.selected:hover .icon--on{color:var(--md-switch-selected-hover-icon-color, var(--md-sys-color-on-primary-container, #21005d))}.selected:focus-within .icon--on{color:var(--md-switch-selected-focus-icon-color, var(--md-sys-color-on-primary-container, #21005d))}.selected:active .icon--on{color:var(--md-switch-selected-pressed-icon-color, var(--md-sys-color-on-primary-container, #21005d))}.selected.disabled .icon--on{color:var(--md-switch-disabled-selected-icon-color, var(--md-sys-color-on-surface, #1d1b20));opacity:var(--md-switch-disabled-selected-icon-opacity, 0.38)}}@layer hcm{@media(forced-colors: active){.icon--off{fill:Canvas}.icon--on{fill:ButtonText}.disabled.unselected .icon--off,.disabled.selected .icon--on{opacity:1}.disabled .icon--on{fill:GrayText}}}
380
- `;
381
-
382
- /**
383
- * @license
384
- * Copyright 2021 Google LLC
385
- * SPDX-License-Identifier: Apache-2.0
386
- */
387
- /**
388
- * @summary Switches toggle the state of a single item on or off.
389
- *
390
- * @description
391
- * There's one type of switch in Material. Use this selection control when the
392
- * user needs to toggle a single item on or off.
393
- *
394
- * @final
395
- * @suppress {visibility}
396
- */
397
- let MdSwitch = class MdSwitch extends Switch {};
398
- MdSwitch.styles = [styles$4];
399
- MdSwitch = __decorate([t('md-switch')], MdSwitch);
400
-
401
- /**
402
- * @license
403
- * Copyright 2023 Google LLC
404
- * SPDX-License-Identifier: Apache-2.0
405
- */
406
- /**
407
- * Creates an event that closes any parent menus.
408
- */
409
- function createCloseMenuEvent(initiator, reason) {
410
- return new CustomEvent('close-menu', {
411
- bubbles: true,
412
- composed: true,
413
- detail: {
414
- initiator,
415
- reason,
416
- itemPath: [initiator]
417
- }
418
- });
419
- }
420
- /**
421
- * Creates a default close menu event used by md-menu.
422
- */
423
- const createDefaultCloseMenuEvent = createCloseMenuEvent;
424
- /**
425
- * Keys that are used for selection in menus.
426
- */
427
- // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
428
- const SelectionKey = {
429
- SPACE: 'Space',
430
- ENTER: 'Enter'
431
- };
432
- /**
433
- * Default close `Reason` kind values.
434
- */
435
- // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
436
- const CloseReason = {
437
- CLICK_SELECTION: 'click-selection',
438
- KEYDOWN: 'keydown'
439
- };
440
- /**
441
- * Keys that can close menus.
442
- */
443
- // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
444
- const KeydownCloseKey = {
445
- ESCAPE: 'Escape',
446
- SPACE: SelectionKey.SPACE,
447
- ENTER: SelectionKey.ENTER
448
- };
449
- /**
450
- * Determines whether the given key code is a key code that should close the
451
- * menu.
452
- *
453
- * @param code The KeyboardEvent code to check.
454
- * @return Whether or not the key code is in the predetermined list to close the
455
- * menu.
456
- */
457
- function isClosableKey(code) {
458
- return Object.values(KeydownCloseKey).some(value => value === code);
459
- }
460
- /**
461
- * Determines whether the given key code is a key code that should select a menu
462
- * item.
463
- *
464
- * @param code They KeyboardEvent code to check.
465
- * @return Whether or not the key code is in the predetermined list to select a
466
- * menu item.
467
- */
468
- function isSelectableKey(code) {
469
- return Object.values(SelectionKey).some(value => value === code);
470
- }
471
- /**
472
- * Determines whether a target element is contained inside another element's
473
- * composed tree.
474
- *
475
- * @param target The potential contained element.
476
- * @param container The potential containing element of the target.
477
- * @returns Whether the target element is contained inside the container's
478
- * composed subtree
479
- */
480
- function isElementInSubtree(target, container) {
481
- // Dispatch a composed, bubbling event to check its path to see if the
482
- // newly-focused element is contained in container's subtree
483
- const focusEv = new Event('md-contains', {
484
- bubbles: true,
485
- composed: true
486
- });
487
- let composedPath = [];
488
- const listener = ev => {
489
- composedPath = ev.composedPath();
490
- };
491
- container.addEventListener('md-contains', listener);
492
- target.dispatchEvent(focusEv);
493
- container.removeEventListener('md-contains', listener);
494
- const isContained = composedPath.length > 0;
495
- return isContained;
496
- }
497
- /**
498
- * Element to focus on when menu is first opened.
499
- */
500
- // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
501
- const FocusState = {
502
- NONE: 'none',
503
- LIST_ROOT: 'list-root',
504
- FIRST_ITEM: 'first-item',
505
- LAST_ITEM: 'last-item'
506
- };
507
-
508
- /**
509
- * @license
510
- * Copyright 2023 Google LLC
511
- * SPDX-License-Identifier: Apache-2.0
512
- */
513
- /**
514
- * An enum of supported Menu corners
515
- */
516
- // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
517
- const Corner = {
518
- END_START: 'end-start',
519
- START_START: 'start-start'};
520
- /**
521
- * Given a surface, an anchor, corners, and some options, this surface will
522
- * calculate the position of a surface to align the two given corners and keep
523
- * the surface inside the window viewport. It also provides a StyleInfo map that
524
- * can be applied to the surface to handle visiblility and position.
525
- */
526
- class SurfacePositionController {
527
- /**
528
- * @param host The host to connect the controller to.
529
- * @param getProperties A function that returns the properties for the
530
- * controller.
531
- */
532
- constructor(host, getProperties) {
533
- this.host = host;
534
- this.getProperties = getProperties;
535
- // The current styles to apply to the surface.
536
- this.surfaceStylesInternal = {
537
- 'display': 'none'
538
- };
539
- // Previous values stored for change detection. Open change detection is
540
- // calculated separately so initialize it here.
541
- this.lastValues = {
542
- isOpen: false
543
- };
544
- this.host.addController(this);
545
- }
546
- /**
547
- * The StyleInfo map to apply to the surface via Lit's stylemap
548
- */
549
- get surfaceStyles() {
550
- return this.surfaceStylesInternal;
551
- }
552
- /**
553
- * Calculates the surface's new position required so that the surface's
554
- * `surfaceCorner` aligns to the anchor's `anchorCorner` while keeping the
555
- * surface inside the window viewport. This positioning also respects RTL by
556
- * checking `getComputedStyle()` on the surface element.
557
- */
558
- async position() {
559
- const {
560
- surfaceEl,
561
- anchorEl,
562
- anchorCorner: anchorCornerRaw,
563
- surfaceCorner: surfaceCornerRaw,
564
- positioning,
565
- xOffset,
566
- yOffset,
567
- disableBlockFlip,
568
- disableInlineFlip,
569
- repositionStrategy
570
- } = this.getProperties();
571
- const anchorCorner = anchorCornerRaw.toLowerCase().trim();
572
- const surfaceCorner = surfaceCornerRaw.toLowerCase().trim();
573
- if (!surfaceEl || !anchorEl) {
574
- return;
575
- }
576
- // Store these before we potentially resize the window with the next set of
577
- // lines
578
- const windowInnerWidth = window.innerWidth;
579
- const windowInnerHeight = window.innerHeight;
580
- const div = document.createElement('div');
581
- div.style.opacity = '0';
582
- div.style.position = 'fixed';
583
- div.style.display = 'block';
584
- div.style.inset = '0';
585
- document.body.appendChild(div);
586
- const scrollbarTestRect = div.getBoundingClientRect();
587
- div.remove();
588
- // Calculate the widths of the scrollbars in the inline and block directions
589
- // to account for window-relative calculations.
590
- const blockScrollbarHeight = window.innerHeight - scrollbarTestRect.bottom;
591
- const inlineScrollbarWidth = window.innerWidth - scrollbarTestRect.right;
592
- // Paint the surface transparently so that we can get the position and the
593
- // rect info of the surface.
594
- this.surfaceStylesInternal = {
595
- 'display': 'block',
596
- 'opacity': '0'
597
- };
598
- // Wait for it to be visible.
599
- this.host.requestUpdate();
600
- await this.host.updateComplete;
601
- // Safari has a bug that makes popovers render incorrectly if the node is
602
- // made visible + Animation Frame before calling showPopover().
603
- // https://bugs.webkit.org/show_bug.cgi?id=264069
604
- // also the cast is required due to differing TS types in Google and OSS.
605
- if (surfaceEl.popover && surfaceEl.isConnected) {
606
- surfaceEl.showPopover();
607
- }
608
- const surfaceRect = surfaceEl.getSurfacePositionClientRect ? surfaceEl.getSurfacePositionClientRect() : surfaceEl.getBoundingClientRect();
609
- const anchorRect = anchorEl.getSurfacePositionClientRect ? anchorEl.getSurfacePositionClientRect() : anchorEl.getBoundingClientRect();
610
- const [surfaceBlock, surfaceInline] = surfaceCorner.split('-');
611
- const [anchorBlock, anchorInline] = anchorCorner.split('-');
612
- // LTR depends on the direction of the SURFACE not the anchor.
613
- const isLTR = getComputedStyle(surfaceEl).direction === 'ltr';
614
- /*
615
- * For more on inline and block dimensions, see MDN article:
616
- * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values
617
- *
618
- * ┌───── inline/blockDocumentOffset inlineScrollbarWidth
619
- * │ │ │
620
- * │ ┌─▼─────┐ │Document
621
- * │ ┌┼───────┴──────────────────────────────┼────────┐
622
- * │ ││ │ │
623
- * └──► ││ ┌───── inline/blockWindowOffset │ │
624
- * ││ │ │ ▼ │
625
- * ││ │ ┌─▼───┐ Window┌┐ │
626
- * └┤ │ ┌┼─────┴───────────────────────┼│ │
627
- * │ │ ││ ││ │
628
- * │ └──► ││ ┌──inline/blockAnchorOffset││ │
629
- * │ ││ │ │ ││ │
630
- * │ └┤ │ ┌──▼───┐ ││ │
631
- * │ │ │ ┌┼──────┤ ││ │
632
- * │ │ └─►│Anchor│ ││ │
633
- * │ │ └┴──────┘ ││ │
634
- * │ │ ││ │
635
- * │ │ ┌───────────────────────┼┼────┐ │
636
- * │ │ │ Surface ││ │ │
637
- * │ │ │ ││ │ │
638
- * │ │ │ ││ │ │
639
- * │ │ │ ││ │ │
640
- * │ │ │ ││ │ │
641
- * │ ┌┼─────┼───────────────────────┼│ │ │
642
- * │ ┌─►┴──────┼────────────────────────┘ ├┐ │
643
- * │ │ │ inline/blockOOBCorrection ││ │
644
- * │ │ │ │ ││ │
645
- * │ │ │ ├──►├│ │
646
- * │ │ │ │ ││ │
647
- * │ │ └────────────────────────┐▼───┼┘ │
648
- * │ blockScrollbarHeight └────┘ │
649
- * │ │
650
- * └───────────────────────────────────────────────┘
651
- */
652
- // Calculate the block positioning properties
653
- let {
654
- blockInset,
655
- blockOutOfBoundsCorrection,
656
- surfaceBlockProperty
657
- } = this.calculateBlock({
658
- surfaceRect,
659
- anchorRect,
660
- anchorBlock,
661
- surfaceBlock,
662
- yOffset,
663
- positioning,
664
- windowInnerHeight,
665
- blockScrollbarHeight
666
- });
667
- // If the surface should be out of bounds in the block direction, flip the
668
- // surface and anchor corner block values and recalculate
669
- if (blockOutOfBoundsCorrection && !disableBlockFlip) {
670
- const flippedSurfaceBlock = surfaceBlock === 'start' ? 'end' : 'start';
671
- const flippedAnchorBlock = anchorBlock === 'start' ? 'end' : 'start';
672
- const flippedBlock = this.calculateBlock({
673
- surfaceRect,
674
- anchorRect,
675
- anchorBlock: flippedAnchorBlock,
676
- surfaceBlock: flippedSurfaceBlock,
677
- yOffset,
678
- positioning,
679
- windowInnerHeight,
680
- blockScrollbarHeight
681
- });
682
- // In the case that the flipped verion would require less out of bounds
683
- // correcting, use the flipped corner block values
684
- if (blockOutOfBoundsCorrection > flippedBlock.blockOutOfBoundsCorrection) {
685
- blockInset = flippedBlock.blockInset;
686
- blockOutOfBoundsCorrection = flippedBlock.blockOutOfBoundsCorrection;
687
- surfaceBlockProperty = flippedBlock.surfaceBlockProperty;
688
- }
689
- }
690
- // Calculate the inline positioning properties
691
- let {
692
- inlineInset,
693
- inlineOutOfBoundsCorrection,
694
- surfaceInlineProperty
695
- } = this.calculateInline({
696
- surfaceRect,
697
- anchorRect,
698
- anchorInline,
699
- surfaceInline,
700
- xOffset,
701
- positioning,
702
- isLTR,
703
- windowInnerWidth,
704
- inlineScrollbarWidth
705
- });
706
- // If the surface should be out of bounds in the inline direction, flip the
707
- // surface and anchor corner inline values and recalculate
708
- if (inlineOutOfBoundsCorrection && !disableInlineFlip) {
709
- const flippedSurfaceInline = surfaceInline === 'start' ? 'end' : 'start';
710
- const flippedAnchorInline = anchorInline === 'start' ? 'end' : 'start';
711
- const flippedInline = this.calculateInline({
712
- surfaceRect,
713
- anchorRect,
714
- anchorInline: flippedAnchorInline,
715
- surfaceInline: flippedSurfaceInline,
716
- xOffset,
717
- positioning,
718
- isLTR,
719
- windowInnerWidth,
720
- inlineScrollbarWidth
721
- });
722
- // In the case that the flipped verion would require less out of bounds
723
- // correcting, use the flipped corner inline values
724
- if (Math.abs(inlineOutOfBoundsCorrection) > Math.abs(flippedInline.inlineOutOfBoundsCorrection)) {
725
- inlineInset = flippedInline.inlineInset;
726
- inlineOutOfBoundsCorrection = flippedInline.inlineOutOfBoundsCorrection;
727
- surfaceInlineProperty = flippedInline.surfaceInlineProperty;
728
- }
729
- }
730
- // If we are simply repositioning the surface back inside the viewport,
731
- // subtract the out of bounds correction values from the positioning.
732
- if (repositionStrategy === 'move') {
733
- blockInset = blockInset - blockOutOfBoundsCorrection;
734
- inlineInset = inlineInset - inlineOutOfBoundsCorrection;
735
- }
736
- this.surfaceStylesInternal = {
737
- 'display': 'block',
738
- 'opacity': '1',
739
- [surfaceBlockProperty]: `${blockInset}px`,
740
- [surfaceInlineProperty]: `${inlineInset}px`
741
- };
742
- // In the case that we are resizing the surface to stay inside the viewport
743
- // we need to set height and width on the surface.
744
- if (repositionStrategy === 'resize') {
745
- // Add a height property to the styles if there is block height correction
746
- if (blockOutOfBoundsCorrection) {
747
- this.surfaceStylesInternal['height'] = `${surfaceRect.height - blockOutOfBoundsCorrection}px`;
748
- }
749
- // Add a width property to the styles if there is block height correction
750
- if (inlineOutOfBoundsCorrection) {
751
- this.surfaceStylesInternal['width'] = `${surfaceRect.width - inlineOutOfBoundsCorrection}px`;
752
- }
753
- }
754
- this.host.requestUpdate();
755
- }
756
- /**
757
- * Calculates the css property, the inset, and the out of bounds correction
758
- * for the surface in the block direction.
759
- */
760
- calculateBlock(config) {
761
- const {
762
- surfaceRect,
763
- anchorRect,
764
- anchorBlock,
765
- surfaceBlock,
766
- yOffset,
767
- positioning,
768
- windowInnerHeight,
769
- blockScrollbarHeight
770
- } = config;
771
- // We use number booleans to multiply values rather than `if` / ternary
772
- // statements because it _heavily_ cuts down on nesting and readability
773
- const relativeToWindow = positioning === 'fixed' || positioning === 'document' ? 1 : 0;
774
- const relativeToDocument = positioning === 'document' ? 1 : 0;
775
- const isSurfaceBlockStart = surfaceBlock === 'start' ? 1 : 0;
776
- const isSurfaceBlockEnd = surfaceBlock === 'end' ? 1 : 0;
777
- const isOneBlockEnd = anchorBlock !== surfaceBlock ? 1 : 0;
778
- // Whether or not to apply the height of the anchor
779
- const blockAnchorOffset = isOneBlockEnd * anchorRect.height + yOffset;
780
- // The absolute block position of the anchor relative to window
781
- const blockTopLayerOffset = isSurfaceBlockStart * anchorRect.top + isSurfaceBlockEnd * (windowInnerHeight - anchorRect.bottom - blockScrollbarHeight);
782
- const blockDocumentOffset = isSurfaceBlockStart * window.scrollY - isSurfaceBlockEnd * window.scrollY;
783
- // If the surface's block would be out of bounds of the window, move it back
784
- // in
785
- const blockOutOfBoundsCorrection = Math.abs(Math.min(0, windowInnerHeight - blockTopLayerOffset - blockAnchorOffset - surfaceRect.height));
786
- // The block logical value of the surface
787
- const blockInset = relativeToWindow * blockTopLayerOffset + relativeToDocument * blockDocumentOffset + blockAnchorOffset;
788
- const surfaceBlockProperty = surfaceBlock === 'start' ? 'inset-block-start' : 'inset-block-end';
789
- return {
790
- blockInset,
791
- blockOutOfBoundsCorrection,
792
- surfaceBlockProperty
793
- };
794
- }
795
- /**
796
- * Calculates the css property, the inset, and the out of bounds correction
797
- * for the surface in the inline direction.
798
- */
799
- calculateInline(config) {
800
- const {
801
- isLTR: isLTRBool,
802
- surfaceInline,
803
- anchorInline,
804
- anchorRect,
805
- surfaceRect,
806
- xOffset,
807
- positioning,
808
- windowInnerWidth,
809
- inlineScrollbarWidth
810
- } = config;
811
- // We use number booleans to multiply values rather than `if` / ternary
812
- // statements because it _heavily_ cuts down on nesting and readability
813
- const relativeToWindow = positioning === 'fixed' || positioning === 'document' ? 1 : 0;
814
- const relativeToDocument = positioning === 'document' ? 1 : 0;
815
- const isLTR = isLTRBool ? 1 : 0;
816
- const isRTL = isLTRBool ? 0 : 1;
817
- const isSurfaceInlineStart = surfaceInline === 'start' ? 1 : 0;
818
- const isSurfaceInlineEnd = surfaceInline === 'end' ? 1 : 0;
819
- const isOneInlineEnd = anchorInline !== surfaceInline ? 1 : 0;
820
- // Whether or not to apply the width of the anchor
821
- const inlineAnchorOffset = isOneInlineEnd * anchorRect.width + xOffset;
822
- // The inline position of the anchor relative to window in LTR
823
- const inlineTopLayerOffsetLTR = isSurfaceInlineStart * anchorRect.left + isSurfaceInlineEnd * (windowInnerWidth - anchorRect.right - inlineScrollbarWidth);
824
- // The inline position of the anchor relative to window in RTL
825
- const inlineTopLayerOffsetRTL = isSurfaceInlineStart * (windowInnerWidth - anchorRect.right - inlineScrollbarWidth) + isSurfaceInlineEnd * anchorRect.left;
826
- // The inline position of the anchor relative to window
827
- const inlineTopLayerOffset = isLTR * inlineTopLayerOffsetLTR + isRTL * inlineTopLayerOffsetRTL;
828
- // The inline position of the anchor relative to window in LTR
829
- const inlineDocumentOffsetLTR = isSurfaceInlineStart * window.scrollX - isSurfaceInlineEnd * window.scrollX;
830
- // The inline position of the anchor relative to window in RTL
831
- const inlineDocumentOffsetRTL = isSurfaceInlineEnd * window.scrollX - isSurfaceInlineStart * window.scrollX;
832
- // The inline position of the anchor relative to window
833
- const inlineDocumentOffset = isLTR * inlineDocumentOffsetLTR + isRTL * inlineDocumentOffsetRTL;
834
- // If the surface's inline would be out of bounds of the window, move it
835
- // back in
836
- const inlineOutOfBoundsCorrection = Math.abs(Math.min(0, windowInnerWidth - inlineTopLayerOffset - inlineAnchorOffset - surfaceRect.width));
837
- // The inline logical value of the surface
838
- const inlineInset = relativeToWindow * inlineTopLayerOffset + inlineAnchorOffset + relativeToDocument * inlineDocumentOffset;
839
- let surfaceInlineProperty = surfaceInline === 'start' ? 'inset-inline-start' : 'inset-inline-end';
840
- // There are cases where the element is RTL but the root of the page is not.
841
- // In these cases we want to not use logical properties.
842
- if (positioning === 'document' || positioning === 'fixed') {
843
- if (surfaceInline === 'start' && isLTRBool || surfaceInline === 'end' && !isLTRBool) {
844
- surfaceInlineProperty = 'left';
845
- } else {
846
- surfaceInlineProperty = 'right';
847
- }
848
- }
849
- return {
850
- inlineInset,
851
- inlineOutOfBoundsCorrection,
852
- surfaceInlineProperty
853
- };
854
- }
855
- hostUpdate() {
856
- this.onUpdate();
857
- }
858
- hostUpdated() {
859
- this.onUpdate();
860
- }
861
- /**
862
- * Checks whether the properties passed into the controller have changed since
863
- * the last positioning. If so, it will reposition if the surface is open or
864
- * close it if the surface should close.
865
- */
866
- async onUpdate() {
867
- const props = this.getProperties();
868
- let hasChanged = false;
869
- for (const [key, value] of Object.entries(props)) {
870
- // tslint:disable-next-line
871
- hasChanged = hasChanged || value !== this.lastValues[key];
872
- if (hasChanged) break;
873
- }
874
- const openChanged = this.lastValues.isOpen !== props.isOpen;
875
- const hasAnchor = !!props.anchorEl;
876
- const hasSurface = !!props.surfaceEl;
877
- if (hasChanged && hasAnchor && hasSurface) {
878
- // Only update isOpen, because if it's closed, we do not want to waste
879
- // time on a useless reposition calculation. So save the other "dirty"
880
- // values until next time it opens.
881
- this.lastValues.isOpen = props.isOpen;
882
- if (props.isOpen) {
883
- // We are going to do a reposition, so save the prop values for future
884
- // dirty checking.
885
- this.lastValues = props;
886
- await this.position();
887
- props.onOpen();
888
- } else if (openChanged) {
889
- await props.beforeClose();
890
- this.close();
891
- props.onClose();
892
- }
893
- }
894
- }
895
- /**
896
- * Hides the surface.
897
- */
898
- close() {
899
- this.surfaceStylesInternal = {
900
- 'display': 'none'
901
- };
902
- this.host.requestUpdate();
903
- const surfaceEl = this.getProperties().surfaceEl;
904
- // The following type casts are required due to differing TS types in Google
905
- // and open source.
906
- if (surfaceEl !== null && surfaceEl !== void 0 && surfaceEl.popover && surfaceEl !== null && surfaceEl !== void 0 && surfaceEl.isConnected) {
907
- surfaceEl.hidePopover();
908
- }
909
- }
910
- }
911
-
912
- /**
913
- * @license
914
- * Copyright 2023 Google LLC
915
- * SPDX-License-Identifier: Apache-2.0
916
- */
917
- /**
918
- * Indicies to access the TypeaheadRecord tuple type.
919
- */
920
- const TYPEAHEAD_RECORD = {
921
- INDEX: 0,
922
- ITEM: 1,
923
- TEXT: 2
924
- };
925
- /**
926
- * This controller listens to `keydown` events and searches the header text of
927
- * an array of `MenuItem`s with the corresponding entered keys within the buffer
928
- * time and activates the item.
929
- *
930
- * @example
931
- * ```ts
932
- * const typeaheadController = new TypeaheadController(() => ({
933
- * typeaheadBufferTime: 50,
934
- * getItems: () => Array.from(document.querySelectorAll('md-menu-item'))
935
- * }));
936
- * html`
937
- * <div
938
- * @keydown=${typeaheadController.onKeydown}
939
- * tabindex="0"
940
- * class="activeItemText">
941
- * <!-- focusable element that will receive keydown events -->
942
- * Apple
943
- * </div>
944
- * <div>
945
- * <md-menu-item active header="Apple"></md-menu-item>
946
- * <md-menu-item header="Apricot"></md-menu-item>
947
- * <md-menu-item header="Banana"></md-menu-item>
948
- * <md-menu-item header="Olive"></md-menu-item>
949
- * <md-menu-item header="Orange"></md-menu-item>
950
- * </div>
951
- * `;
952
- * ```
953
- */
954
- class TypeaheadController {
955
- /**
956
- * @param getProperties A function that returns the options of the typeahead
957
- * controller:
958
- *
959
- * {
960
- * getItems: A function that returns an array of menu items to be searched.
961
- * typeaheadBufferTime: The maximum time between each keystroke to keep the
962
- * current type buffer alive.
963
- * }
964
- */
965
- constructor(getProperties) {
966
- this.getProperties = getProperties;
967
- /**
968
- * Array of tuples that helps with indexing.
969
- */
970
- this.typeaheadRecords = [];
971
- /**
972
- * Currently-typed text since last buffer timeout
973
- */
974
- this.typaheadBuffer = '';
975
- /**
976
- * The timeout id from the current buffer's setTimeout
977
- */
978
- this.cancelTypeaheadTimeout = 0;
979
- /**
980
- * If we are currently "typing"
981
- */
982
- this.isTypingAhead = false;
983
- /**
984
- * The record of the last active item.
985
- */
986
- this.lastActiveRecord = null;
987
- /**
988
- * Apply this listener to the element that will receive `keydown` events that
989
- * should trigger this controller.
990
- *
991
- * @param event The native browser `KeyboardEvent` from the `keydown` event.
992
- */
993
- this.onKeydown = event => {
994
- if (this.isTypingAhead) {
995
- this.typeahead(event);
996
- } else {
997
- this.beginTypeahead(event);
998
- }
999
- };
1000
- /**
1001
- * Ends the current typeahead and clears the buffer.
1002
- */
1003
- this.endTypeahead = () => {
1004
- this.isTypingAhead = false;
1005
- this.typaheadBuffer = '';
1006
- this.typeaheadRecords = [];
1007
- };
1008
- }
1009
- get items() {
1010
- return this.getProperties().getItems();
1011
- }
1012
- get active() {
1013
- return this.getProperties().active;
1014
- }
1015
- /**
1016
- * Sets up typingahead
1017
- */
1018
- beginTypeahead(event) {
1019
- if (!this.active) {
1020
- return;
1021
- }
1022
- // We don't want to typeahead if the _beginning_ of the typeahead is a menu
1023
- // navigation, or a selection. We will handle "Space" only if it's in the
1024
- // middle of a typeahead
1025
- if (event.code === 'Space' || event.code === 'Enter' || event.code.startsWith('Arrow') || event.code === 'Escape') {
1026
- return;
1027
- }
1028
- this.isTypingAhead = true;
1029
- // Generates the record array data structure which is the index, the element
1030
- // and a normalized header.
1031
- this.typeaheadRecords = this.items.map((el, index) => [index, el, el.typeaheadText.trim().toLowerCase()]);
1032
- this.lastActiveRecord = this.typeaheadRecords.find(record => record[TYPEAHEAD_RECORD.ITEM].tabIndex === 0) ?? null;
1033
- if (this.lastActiveRecord) {
1034
- this.lastActiveRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = -1;
1035
- }
1036
- this.typeahead(event);
1037
- }
1038
- /**
1039
- * Performs the typeahead. Based on the normalized items and the current text
1040
- * buffer, finds the _next_ item with matching text and activates it.
1041
- *
1042
- * @example
1043
- *
1044
- * items: Apple, Banana, Olive, Orange, Cucumber
1045
- * buffer: ''
1046
- * user types: o
1047
- *
1048
- * activates Olive
1049
- *
1050
- * @example
1051
- *
1052
- * items: Apple, Banana, Olive (active), Orange, Cucumber
1053
- * buffer: 'o'
1054
- * user types: l
1055
- *
1056
- * activates Olive
1057
- *
1058
- * @example
1059
- *
1060
- * items: Apple, Banana, Olive (active), Orange, Cucumber
1061
- * buffer: ''
1062
- * user types: o
1063
- *
1064
- * activates Orange
1065
- *
1066
- * @example
1067
- *
1068
- * items: Apple, Banana, Olive, Orange (active), Cucumber
1069
- * buffer: ''
1070
- * user types: o
1071
- *
1072
- * activates Olive
1073
- */
1074
- typeahead(event) {
1075
- if (event.defaultPrevented) return;
1076
- clearTimeout(this.cancelTypeaheadTimeout);
1077
- // Stop typingahead if one of the navigation or selection keys (except for
1078
- // Space) are pressed
1079
- if (event.code === 'Enter' || event.code.startsWith('Arrow') || event.code === 'Escape') {
1080
- this.endTypeahead();
1081
- if (this.lastActiveRecord) {
1082
- this.lastActiveRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = -1;
1083
- }
1084
- return;
1085
- }
1086
- // If Space is pressed, prevent it from selecting and closing the menu
1087
- if (event.code === 'Space') {
1088
- event.preventDefault();
1089
- }
1090
- // Start up a new keystroke buffer timeout
1091
- this.cancelTypeaheadTimeout = setTimeout(this.endTypeahead, this.getProperties().typeaheadBufferTime);
1092
- this.typaheadBuffer += event.key.toLowerCase();
1093
- const lastActiveIndex = this.lastActiveRecord ? this.lastActiveRecord[TYPEAHEAD_RECORD.INDEX] : -1;
1094
- const numRecords = this.typeaheadRecords.length;
1095
- /**
1096
- * Sorting function that will resort the items starting with the given index
1097
- *
1098
- * @example
1099
- *
1100
- * this.typeaheadRecords =
1101
- * 0: [0, <reference>, 'apple']
1102
- * 1: [1, <reference>, 'apricot']
1103
- * 2: [2, <reference>, 'banana']
1104
- * 3: [3, <reference>, 'olive'] <-- lastActiveIndex
1105
- * 4: [4, <reference>, 'orange']
1106
- * 5: [5, <reference>, 'strawberry']
1107
- *
1108
- * this.typeaheadRecords.sort((a,b) => rebaseIndexOnActive(a)
1109
- * - rebaseIndexOnActive(b)) ===
1110
- * 0: [3, <reference>, 'olive'] <-- lastActiveIndex
1111
- * 1: [4, <reference>, 'orange']
1112
- * 2: [5, <reference>, 'strawberry']
1113
- * 3: [0, <reference>, 'apple']
1114
- * 4: [1, <reference>, 'apricot']
1115
- * 5: [2, <reference>, 'banana']
1116
- */
1117
- const rebaseIndexOnActive = record => {
1118
- return (record[TYPEAHEAD_RECORD.INDEX] + numRecords - lastActiveIndex) % numRecords;
1119
- };
1120
- // records filtered and sorted / rebased around the last active index
1121
- const matchingRecords = this.typeaheadRecords.filter(record => !record[TYPEAHEAD_RECORD.ITEM].disabled && record[TYPEAHEAD_RECORD.TEXT].startsWith(this.typaheadBuffer)).sort((a, b) => rebaseIndexOnActive(a) - rebaseIndexOnActive(b));
1122
- // Just leave if there's nothing that matches. Native select will just
1123
- // choose the first thing that starts with the next letter in the alphabet
1124
- // but that's out of scope and hard to localize
1125
- if (matchingRecords.length === 0) {
1126
- clearTimeout(this.cancelTypeaheadTimeout);
1127
- if (this.lastActiveRecord) {
1128
- this.lastActiveRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = -1;
1129
- }
1130
- this.endTypeahead();
1131
- return;
1132
- }
1133
- const isNewQuery = this.typaheadBuffer.length === 1;
1134
- let nextRecord;
1135
- // This is likely the case that someone is trying to "tab" through different
1136
- // entries that start with the same letter
1137
- if (this.lastActiveRecord === matchingRecords[0] && isNewQuery) {
1138
- nextRecord = matchingRecords[1] ?? matchingRecords[0];
1139
- } else {
1140
- nextRecord = matchingRecords[0];
1141
- }
1142
- if (this.lastActiveRecord) {
1143
- this.lastActiveRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = -1;
1144
- }
1145
- this.lastActiveRecord = nextRecord;
1146
- nextRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = 0;
1147
- nextRecord[TYPEAHEAD_RECORD.ITEM].focus();
1148
- return;
1149
- }
1150
- }
1151
-
1152
- /**
1153
- * @license
1154
- * Copyright 2023 Google LLC
1155
- * SPDX-License-Identifier: Apache-2.0
1156
- */
1157
- /**
1158
- * The default value for the typeahead buffer time in Milliseconds.
1159
- */
1160
- const DEFAULT_TYPEAHEAD_BUFFER_TIME = 200;
1161
- const submenuNavKeys = new Set([NavigableKeys.ArrowDown, NavigableKeys.ArrowUp, NavigableKeys.Home, NavigableKeys.End]);
1162
- const menuNavKeys = new Set([NavigableKeys.ArrowLeft, NavigableKeys.ArrowRight, ...submenuNavKeys]);
1163
- /**
1164
- * Gets the currently focused element on the page.
1165
- *
1166
- * @param activeDoc The document or shadowroot from which to start the search.
1167
- * Defaults to `window.document`
1168
- * @return Returns the currently deeply focused element or `null` if none.
1169
- */
1170
- function getFocusedElement(activeDoc = document) {
1171
- let activeEl = activeDoc.activeElement;
1172
- // Check for activeElement in the case that an element with a shadow root host
1173
- // is currently focused.
1174
- while (activeEl && (_activeEl = activeEl) !== null && _activeEl !== void 0 && (_activeEl = _activeEl.shadowRoot) !== null && _activeEl !== void 0 && _activeEl.activeElement) {
1175
- var _activeEl;
1176
- activeEl = activeEl.shadowRoot.activeElement;
1177
- }
1178
- return activeEl;
1179
- }
1180
- /**
1181
- * @fires opening {Event} Fired before the opening animation begins
1182
- * @fires opened {Event} Fired once the menu is open, after any animations
1183
- * @fires closing {Event} Fired before the closing animation begins
1184
- * @fires closed {Event} Fired once the menu is closed, after any animations
1185
- */
1186
- class Menu extends i {
1187
- /**
1188
- * Whether the menu is animating upwards or downwards when opening. This is
1189
- * helpful for calculating some animation calculations.
1190
- */
1191
- get openDirection() {
1192
- const menuCornerBlock = this.menuCorner.split('-')[0];
1193
- return menuCornerBlock === 'start' ? 'DOWN' : 'UP';
1194
- }
1195
- /**
1196
- * The element which the menu should align to. If `anchor` is set to a
1197
- * non-empty idref string, then `anchorEl` will resolve to the element with
1198
- * the given id in the same root node. Otherwise, `null`.
1199
- */
1200
- get anchorElement() {
1201
- if (this.anchor) {
1202
- return this.getRootNode().querySelector(`#${this.anchor}`);
1203
- }
1204
- return this.currentAnchorElement;
1205
- }
1206
- set anchorElement(element) {
1207
- this.currentAnchorElement = element;
1208
- this.requestUpdate('anchorElement');
1209
- }
1210
- constructor() {
1211
- super();
1212
- /**
1213
- * The ID of the element in the same root node in which the menu should align
1214
- * to. Overrides setting `anchorElement = elementReference`.
1215
- *
1216
- * __NOTE__: anchor or anchorElement must either be an HTMLElement or resolve
1217
- * to an HTMLElement in order for menu to open.
1218
- */
1219
- this.anchor = '';
1220
- /**
1221
- * Whether the positioning algorithm should calculate relative to the parent
1222
- * of the anchor element (`absolute`), relative to the window (`fixed`), or
1223
- * relative to the document (`document`). `popover` will use the popover API
1224
- * to render the menu in the top-layer. If your browser does not support the
1225
- * popover API, it will fall back to `fixed`.
1226
- *
1227
- * __Examples for `position = 'fixed'`:__
1228
- *
1229
- * - If there is no `position:relative` in the given parent tree and the
1230
- * surface is `position:absolute`
1231
- * - If the surface is `position:fixed`
1232
- * - If the surface is in the "top layer"
1233
- * - The anchor and the surface do not share a common `position:relative`
1234
- * ancestor
1235
- *
1236
- * When using `positioning=fixed`, in most cases, the menu should position
1237
- * itself above most other `position:absolute` or `position:fixed` elements
1238
- * when placed inside of them. e.g. using a menu inside of an `md-dialog`.
1239
- *
1240
- * __NOTE__: Fixed menus will not scroll with the page and will be fixed to
1241
- * the window instead.
1242
- *
1243
- * __Examples for `position = 'document'`:__
1244
- *
1245
- * - There is no parent that creates a relative positioning context e.g.
1246
- * `position: relative`, `position: absolute`, `transform: translate(x, y)`,
1247
- * etc.
1248
- * - You put the effort into hoisting the menu to the top of the DOM like the
1249
- * end of the `<body>` to render over everything or in a top-layer.
1250
- * - You are reusing a single `md-menu` element that dynamically renders
1251
- * content.
1252
- *
1253
- * __Examples for `position = 'popover'`:__
1254
- *
1255
- * - Your browser supports `popover`.
1256
- * - Most cases. Once popover is in browsers, this will become the default.
1257
- */
1258
- this.positioning = 'absolute';
1259
- /**
1260
- * Skips the opening and closing animations.
1261
- */
1262
- this.quick = false;
1263
- /**
1264
- * Displays overflow content like a submenu. Not required in most cases when
1265
- * using `positioning="popover"`.
1266
- *
1267
- * __NOTE__: This may cause adverse effects if you set
1268
- * `md-menu {max-height:...}`
1269
- * and have items overflowing items in the "y" direction.
1270
- */
1271
- this.hasOverflow = false;
1272
- /**
1273
- * Opens the menu and makes it visible. Alternative to the `.show()` and
1274
- * `.close()` methods
1275
- */
1276
- this.open = false;
1277
- /**
1278
- * Offsets the menu's inline alignment from the anchor by the given number in
1279
- * pixels. This value is direction aware and will follow the LTR / RTL
1280
- * direction.
1281
- *
1282
- * e.g. LTR: positive -> right, negative -> left
1283
- * RTL: positive -> left, negative -> right
1284
- */
1285
- this.xOffset = 0;
1286
- /**
1287
- * Offsets the menu's block alignment from the anchor by the given number in
1288
- * pixels.
1289
- *
1290
- * e.g. positive -> down, negative -> up
1291
- */
1292
- this.yOffset = 0;
1293
- /**
1294
- * Disable the `flip` behavior that usually happens on the horizontal axis
1295
- * when the surface would render outside the viewport.
1296
- */
1297
- this.noHorizontalFlip = false;
1298
- /**
1299
- * Disable the `flip` behavior that usually happens on the vertical axis when
1300
- * the surface would render outside the viewport.
1301
- */
1302
- this.noVerticalFlip = false;
1303
- /**
1304
- * The max time between the keystrokes of the typeahead menu behavior before
1305
- * it clears the typeahead buffer.
1306
- */
1307
- this.typeaheadDelay = DEFAULT_TYPEAHEAD_BUFFER_TIME;
1308
- /**
1309
- * The corner of the anchor which to align the menu in the standard logical
1310
- * property style of <block>-<inline> e.g. `'end-start'`.
1311
- *
1312
- * NOTE: This value may not be respected by the menu positioning algorithm
1313
- * if the menu would render outisde the viewport.
1314
- * Use `no-horizontal-flip` or `no-vertical-flip` to force the usage of the value
1315
- */
1316
- this.anchorCorner = Corner.END_START;
1317
- /**
1318
- * The corner of the menu which to align the anchor in the standard logical
1319
- * property style of <block>-<inline> e.g. `'start-start'`.
1320
- *
1321
- * NOTE: This value may not be respected by the menu positioning algorithm
1322
- * if the menu would render outisde the viewport.
1323
- * Use `no-horizontal-flip` or `no-vertical-flip` to force the usage of the value
1324
- */
1325
- this.menuCorner = Corner.START_START;
1326
- /**
1327
- * Keeps the user clicks outside the menu.
1328
- *
1329
- * NOTE: clicking outside may still cause focusout to close the menu so see
1330
- * `stayOpenOnFocusout`.
1331
- */
1332
- this.stayOpenOnOutsideClick = false;
1333
- /**
1334
- * Keeps the menu open when focus leaves the menu's composed subtree.
1335
- *
1336
- * NOTE: Focusout behavior will stop propagation of the focusout event. Set
1337
- * this property to true to opt-out of menu's focusout handling altogether.
1338
- */
1339
- this.stayOpenOnFocusout = false;
1340
- /**
1341
- * After closing, does not restore focus to the last focused element before
1342
- * the menu was opened.
1343
- */
1344
- this.skipRestoreFocus = false;
1345
- /**
1346
- * The element that should be focused by default once opened.
1347
- *
1348
- * NOTE: When setting default focus to 'LIST_ROOT', remember to change
1349
- * `tabindex` to `0` and change md-menu's display to something other than
1350
- * `display: contents` when necessary.
1351
- */
1352
- this.defaultFocus = FocusState.FIRST_ITEM;
1353
- /**
1354
- * Turns off navigation wrapping. By default, navigating past the end of the
1355
- * menu items will wrap focus back to the beginning and vice versa. Use this
1356
- * for ARIA patterns that do not wrap focus, like combobox.
1357
- */
1358
- this.noNavigationWrap = false;
1359
- this.typeaheadActive = true;
1360
- /**
1361
- * Whether or not the current menu is a submenu and should not handle specific
1362
- * navigation keys.
1363
- *
1364
- * @export
1365
- */
1366
- this.isSubmenu = false;
1367
- /**
1368
- * The event path of the last window pointerdown event.
1369
- */
1370
- this.pointerPath = [];
1371
- /**
1372
- * Whether or not the menu is repositoining due to window / document resize
1373
- */
1374
- this.isRepositioning = false;
1375
- this.openCloseAnimationSignal = createAnimationSignal();
1376
- this.listController = new ListController({
1377
- isItem: maybeItem => {
1378
- return maybeItem.hasAttribute('md-menu-item');
1379
- },
1380
- getPossibleItems: () => this.slotItems,
1381
- isRtl: () => getComputedStyle(this).direction === 'rtl',
1382
- deactivateItem: item => {
1383
- item.selected = false;
1384
- item.tabIndex = -1;
1385
- },
1386
- activateItem: item => {
1387
- item.selected = true;
1388
- item.tabIndex = 0;
1389
- },
1390
- isNavigableKey: key => {
1391
- if (!this.isSubmenu) {
1392
- return menuNavKeys.has(key);
1393
- }
1394
- const isRtl = getComputedStyle(this).direction === 'rtl';
1395
- // we want md-submenu to handle the submenu's left/right arrow exit
1396
- // key so it can close the menu instead of navigate the list.
1397
- // Therefore we need to include all keys but left/right arrow close
1398
- // key
1399
- const arrowOpen = isRtl ? NavigableKeys.ArrowLeft : NavigableKeys.ArrowRight;
1400
- if (key === arrowOpen) {
1401
- return true;
1402
- }
1403
- return submenuNavKeys.has(key);
1404
- },
1405
- wrapNavigation: () => !this.noNavigationWrap
1406
- });
1407
- /**
1408
- * The element that was focused before the menu opened.
1409
- */
1410
- this.lastFocusedElement = null;
1411
- /**
1412
- * Handles typeahead navigation through the menu.
1413
- */
1414
- this.typeaheadController = new TypeaheadController(() => {
1415
- return {
1416
- getItems: () => this.items,
1417
- typeaheadBufferTime: this.typeaheadDelay,
1418
- active: this.typeaheadActive
1419
- };
1420
- });
1421
- this.currentAnchorElement = null;
1422
- this.internals =
1423
- // Cast needed for closure
1424
- this.attachInternals();
1425
- /**
1426
- * Handles positioning the surface and aligning it to the anchor as well as
1427
- * keeping it in the viewport.
1428
- */
1429
- this.menuPositionController = new SurfacePositionController(this, () => {
1430
- return {
1431
- anchorCorner: this.anchorCorner,
1432
- surfaceCorner: this.menuCorner,
1433
- surfaceEl: this.surfaceEl,
1434
- anchorEl: this.anchorElement,
1435
- positioning: this.positioning === 'popover' ? 'document' : this.positioning,
1436
- isOpen: this.open,
1437
- xOffset: this.xOffset,
1438
- yOffset: this.yOffset,
1439
- disableBlockFlip: this.noVerticalFlip,
1440
- disableInlineFlip: this.noHorizontalFlip,
1441
- onOpen: this.onOpened,
1442
- beforeClose: this.beforeClose,
1443
- onClose: this.onClosed,
1444
- // We can't resize components that have overflow like menus with
1445
- // submenus because the overflow-y will show menu items / content
1446
- // outside the bounds of the menu. Popover API fixes this because each
1447
- // submenu is hoisted to the top-layer and are not considered overflow
1448
- // content.
1449
- repositionStrategy: this.hasOverflow && this.positioning !== 'popover' ? 'move' : 'resize'
1450
- };
1451
- });
1452
- this.onWindowResize = () => {
1453
- if (this.isRepositioning || this.positioning !== 'document' && this.positioning !== 'fixed' && this.positioning !== 'popover') {
1454
- return;
1455
- }
1456
- this.isRepositioning = true;
1457
- this.reposition();
1458
- this.isRepositioning = false;
1459
- };
1460
- this.handleFocusout = async event => {
1461
- const anchorEl = this.anchorElement;
1462
- // Do not close if we focused out by clicking on the anchor element. We
1463
- // can't assume anchor buttons can be the related target because of iOS does
1464
- // not focus buttons.
1465
- if (this.stayOpenOnFocusout || !this.open || this.pointerPath.includes(anchorEl)) {
1466
- return;
1467
- }
1468
- if (event.relatedTarget) {
1469
- // Don't close the menu if we are switching focus between menu,
1470
- // md-menu-item, and md-list or if the anchor was click focused, but check
1471
- // if length of pointerPath is 0 because that means something was at least
1472
- // clicked (shift+tab case).
1473
- if (isElementInSubtree(event.relatedTarget, this) || this.pointerPath.length !== 0 && isElementInSubtree(event.relatedTarget, anchorEl)) {
1474
- return;
1475
- }
1476
- } else if (this.pointerPath.includes(this)) {
1477
- // If menu tabindex == -1 and the user clicks on the menu or a divider, we
1478
- // want to keep the menu open.
1479
- return;
1480
- }
1481
- const oldRestoreFocus = this.skipRestoreFocus;
1482
- // allow focus to continue to the next focused object rather than returning
1483
- this.skipRestoreFocus = true;
1484
- this.close();
1485
- // await for close
1486
- await this.updateComplete;
1487
- // return to previous behavior
1488
- this.skipRestoreFocus = oldRestoreFocus;
1489
- };
1490
- /**
1491
- * Saves the last focused element focuses the new element based on
1492
- * `defaultFocus`, and animates open.
1493
- */
1494
- this.onOpened = async () => {
1495
- this.lastFocusedElement = getFocusedElement();
1496
- const items = this.items;
1497
- const activeItemRecord = getActiveItem(items);
1498
- if (activeItemRecord && this.defaultFocus !== FocusState.NONE) {
1499
- activeItemRecord.item.tabIndex = -1;
1500
- }
1501
- let animationAborted = !this.quick;
1502
- if (this.quick) {
1503
- this.dispatchEvent(new Event('opening'));
1504
- } else {
1505
- animationAborted = !!(await this.animateOpen());
1506
- }
1507
- // This must come after the opening animation or else it may focus one of
1508
- // the items before the animation has begun and causes the list to slide
1509
- // (block-padding-of-the-menu)px at the end of the animation
1510
- switch (this.defaultFocus) {
1511
- case FocusState.FIRST_ITEM:
1512
- const first = getFirstActivatableItem(items);
1513
- if (first) {
1514
- first.tabIndex = 0;
1515
- first.focus();
1516
- await first.updateComplete;
1517
- }
1518
- break;
1519
- case FocusState.LAST_ITEM:
1520
- const last = getLastActivatableItem(items);
1521
- if (last) {
1522
- last.tabIndex = 0;
1523
- last.focus();
1524
- await last.updateComplete;
1525
- }
1526
- break;
1527
- case FocusState.LIST_ROOT:
1528
- this.focus();
1529
- break;
1530
- default:
1531
- case FocusState.NONE:
1532
- // Do nothing.
1533
- break;
1534
- }
1535
- if (!animationAborted) {
1536
- this.dispatchEvent(new Event('opened'));
1537
- }
1538
- };
1539
- /**
1540
- * Animates closed.
1541
- */
1542
- this.beforeClose = async () => {
1543
- this.open = false;
1544
- if (!this.skipRestoreFocus) {
1545
- var _this$lastFocusedElem, _this$lastFocusedElem2;
1546
- (_this$lastFocusedElem = this.lastFocusedElement) === null || _this$lastFocusedElem === void 0 || (_this$lastFocusedElem2 = _this$lastFocusedElem.focus) === null || _this$lastFocusedElem2 === void 0 || _this$lastFocusedElem2.call(_this$lastFocusedElem);
1547
- }
1548
- if (!this.quick) {
1549
- await this.animateClose();
1550
- }
1551
- };
1552
- /**
1553
- * Focuses the last focused element.
1554
- */
1555
- this.onClosed = () => {
1556
- if (this.quick) {
1557
- this.dispatchEvent(new Event('closing'));
1558
- this.dispatchEvent(new Event('closed'));
1559
- }
1560
- };
1561
- this.onWindowPointerdown = event => {
1562
- this.pointerPath = event.composedPath();
1563
- };
1564
- /**
1565
- * We cannot listen to window click because Safari on iOS will not bubble a
1566
- * click event on window if the item clicked is not a "clickable" item such as
1567
- * <body>
1568
- */
1569
- this.onDocumentClick = event => {
1570
- if (!this.open) {
1571
- return;
1572
- }
1573
- const path = event.composedPath();
1574
- if (!this.stayOpenOnOutsideClick && !path.includes(this) && !path.includes(this.anchorElement)) {
1575
- this.open = false;
1576
- }
1577
- };
1578
- {
1579
- this.internals.role = 'menu';
1580
- this.addEventListener('keydown', this.handleKeydown);
1581
- // Capture so that we can grab the event before it reaches the menu item
1582
- // istelf. Specifically useful for the case where typeahead encounters a
1583
- // space and we don't want the menu item to close the menu.
1584
- this.addEventListener('keydown', this.captureKeydown, {
1585
- capture: true
1586
- });
1587
- this.addEventListener('focusout', this.handleFocusout);
1588
- }
1589
- }
1590
- /**
1591
- * The menu items associated with this menu. The items must be `MenuItem`s and
1592
- * have both the `md-menu-item` and `md-list-item` attributes.
1593
- */
1594
- get items() {
1595
- return this.listController.items;
1596
- }
1597
- willUpdate(changed) {
1598
- if (!changed.has('open')) {
1599
- return;
1600
- }
1601
- if (this.open) {
1602
- this.removeAttribute('aria-hidden');
1603
- return;
1604
- }
1605
- this.setAttribute('aria-hidden', 'true');
1606
- }
1607
- update(changed) {
1608
- if (changed.has('open')) {
1609
- if (this.open) {
1610
- this.setUpGlobalEventListeners();
1611
- } else {
1612
- this.cleanUpGlobalEventListeners();
1613
- }
1614
- }
1615
- // Firefox does not support popover. Fall-back to using fixed.
1616
- if (changed.has('positioning') && this.positioning === 'popover' &&
1617
- // type required for Google JS conformance
1618
- !this.showPopover) {
1619
- this.positioning = 'fixed';
1620
- }
1621
- super.update(changed);
1622
- }
1623
- connectedCallback() {
1624
- super.connectedCallback();
1625
- if (this.open) {
1626
- this.setUpGlobalEventListeners();
1627
- }
1628
- }
1629
- disconnectedCallback() {
1630
- super.disconnectedCallback();
1631
- this.cleanUpGlobalEventListeners();
1632
- }
1633
- getBoundingClientRect() {
1634
- if (!this.surfaceEl) {
1635
- return super.getBoundingClientRect();
1636
- }
1637
- return this.surfaceEl.getBoundingClientRect();
1638
- }
1639
- getClientRects() {
1640
- if (!this.surfaceEl) {
1641
- return super.getClientRects();
1642
- }
1643
- return this.surfaceEl.getClientRects();
1644
- }
1645
- render() {
1646
- return this.renderSurface();
1647
- }
1648
- /**
1649
- * Renders the positionable surface element and its contents.
1650
- */
1651
- renderSurface() {
1652
- return b`
1653
- <div
1654
- class="menu ${e$2(this.getSurfaceClasses())}"
1655
- style=${o$1(this.menuPositionController.surfaceStyles)}
1656
- popover=${this.positioning === 'popover' ? 'manual' : A}>
1657
- ${this.renderElevation()}
1658
- <div class="items">
1659
- <div class="item-padding"> ${this.renderMenuItems()} </div>
1660
- </div>
1661
- </div>
1662
- `;
1663
- }
1664
- /**
1665
- * Renders the menu items' slot
1666
- */
1667
- renderMenuItems() {
1668
- return b`<slot
1669
- @close-menu=${this.onCloseMenu}
1670
- @deactivate-items=${this.onDeactivateItems}
1671
- @request-activation=${this.onRequestActivation}
1672
- @deactivate-typeahead=${this.handleDeactivateTypeahead}
1673
- @activate-typeahead=${this.handleActivateTypeahead}
1674
- @stay-open-on-focusout=${this.handleStayOpenOnFocusout}
1675
- @close-on-focusout=${this.handleCloseOnFocusout}
1676
- @slotchange=${this.listController.onSlotchange}></slot>`;
1677
- }
1678
- /**
1679
- * Renders the elevation component.
1680
- */
1681
- renderElevation() {
1682
- return b`<md-elevation part="elevation"></md-elevation>`;
1683
- }
1684
- getSurfaceClasses() {
1685
- return {
1686
- open: this.open,
1687
- fixed: this.positioning === 'fixed',
1688
- 'has-overflow': this.hasOverflow
1689
- };
1690
- }
1691
- captureKeydown(event) {
1692
- if (event.target === this && !event.defaultPrevented && isClosableKey(event.code)) {
1693
- event.preventDefault();
1694
- this.close();
1695
- }
1696
- this.typeaheadController.onKeydown(event);
1697
- }
1698
- /**
1699
- * Performs the opening animation:
1700
- *
1701
- * https://direct.googleplex.com/#/spec/295000003+271060003
1702
- *
1703
- * @return A promise that resolve to `true` if the animation was aborted,
1704
- * `false` if it was not aborted.
1705
- */
1706
- async animateOpen() {
1707
- const surfaceEl = this.surfaceEl;
1708
- const slotEl = this.slotEl;
1709
- if (!surfaceEl || !slotEl) return true;
1710
- const openDirection = this.openDirection;
1711
- this.dispatchEvent(new Event('opening'));
1712
- // needs to be imperative because we don't want to mix animation and Lit
1713
- // render timing
1714
- surfaceEl.classList.toggle('animating', true);
1715
- const signal = this.openCloseAnimationSignal.start();
1716
- const height = surfaceEl.offsetHeight;
1717
- const openingUpwards = openDirection === 'UP';
1718
- const children = this.items;
1719
- const FULL_DURATION = 500;
1720
- const SURFACE_OPACITY_DURATION = 50;
1721
- const ITEM_OPACITY_DURATION = 250;
1722
- // We want to fit every child fade-in animation within the full duration of
1723
- // the animation.
1724
- const DELAY_BETWEEN_ITEMS = (FULL_DURATION - ITEM_OPACITY_DURATION) / children.length;
1725
- const surfaceHeightAnimation = surfaceEl.animate([{
1726
- height: '0px'
1727
- }, {
1728
- height: `${height}px`
1729
- }], {
1730
- duration: FULL_DURATION,
1731
- easing: EASING.EMPHASIZED
1732
- });
1733
- // When we are opening upwards, we want to make sure the last item is always
1734
- // in view, so we need to translate it upwards the opposite direction of the
1735
- // height animation
1736
- const upPositionCorrectionAnimation = slotEl.animate([{
1737
- transform: openingUpwards ? `translateY(-${height}px)` : ''
1738
- }, {
1739
- transform: ''
1740
- }], {
1741
- duration: FULL_DURATION,
1742
- easing: EASING.EMPHASIZED
1743
- });
1744
- const surfaceOpacityAnimation = surfaceEl.animate([{
1745
- opacity: 0
1746
- }, {
1747
- opacity: 1
1748
- }], SURFACE_OPACITY_DURATION);
1749
- const childrenAnimations = [];
1750
- for (let i = 0; i < children.length; i++) {
1751
- // If we are animating upwards, then reverse the children list.
1752
- const directionalIndex = openingUpwards ? children.length - 1 - i : i;
1753
- const child = children[directionalIndex];
1754
- const animation = child.animate([{
1755
- opacity: 0
1756
- }, {
1757
- opacity: 1
1758
- }], {
1759
- duration: ITEM_OPACITY_DURATION,
1760
- delay: DELAY_BETWEEN_ITEMS * i
1761
- });
1762
- // Make them all initially hidden and then clean up at the end of each
1763
- // animation.
1764
- child.classList.toggle('md-menu-hidden', true);
1765
- animation.addEventListener('finish', () => {
1766
- child.classList.toggle('md-menu-hidden', false);
1767
- });
1768
- childrenAnimations.push([child, animation]);
1769
- }
1770
- let resolveAnimation = value => {};
1771
- const animationFinished = new Promise(resolve => {
1772
- resolveAnimation = resolve;
1773
- });
1774
- signal.addEventListener('abort', () => {
1775
- surfaceHeightAnimation.cancel();
1776
- upPositionCorrectionAnimation.cancel();
1777
- surfaceOpacityAnimation.cancel();
1778
- childrenAnimations.forEach(([child, animation]) => {
1779
- child.classList.toggle('md-menu-hidden', false);
1780
- animation.cancel();
1781
- });
1782
- resolveAnimation(true);
1783
- });
1784
- surfaceHeightAnimation.addEventListener('finish', () => {
1785
- surfaceEl.classList.toggle('animating', false);
1786
- this.openCloseAnimationSignal.finish();
1787
- resolveAnimation(false);
1788
- });
1789
- return await animationFinished;
1790
- }
1791
- /**
1792
- * Performs the closing animation:
1793
- *
1794
- * https://direct.googleplex.com/#/spec/295000003+271060003
1795
- */
1796
- animateClose() {
1797
- let resolve;
1798
- // This promise blocks the surface position controller from setting
1799
- // display: none on the surface which will interfere with this animation.
1800
- const animationEnded = new Promise(res => {
1801
- resolve = res;
1802
- });
1803
- const surfaceEl = this.surfaceEl;
1804
- const slotEl = this.slotEl;
1805
- if (!surfaceEl || !slotEl) {
1806
- resolve(false);
1807
- return animationEnded;
1808
- }
1809
- const openDirection = this.openDirection;
1810
- const closingDownwards = openDirection === 'UP';
1811
- this.dispatchEvent(new Event('closing'));
1812
- // needs to be imperative because we don't want to mix animation and Lit
1813
- // render timing
1814
- surfaceEl.classList.toggle('animating', true);
1815
- const signal = this.openCloseAnimationSignal.start();
1816
- const height = surfaceEl.offsetHeight;
1817
- const children = this.items;
1818
- const FULL_DURATION = 150;
1819
- const SURFACE_OPACITY_DURATION = 50;
1820
- // The surface fades away at the very end
1821
- const SURFACE_OPACITY_DELAY = FULL_DURATION - SURFACE_OPACITY_DURATION;
1822
- const ITEM_OPACITY_DURATION = 50;
1823
- const ITEM_OPACITY_INITIAL_DELAY = 50;
1824
- const END_HEIGHT_PERCENTAGE = 0.35;
1825
- // We want to fit every child fade-out animation within the full duration of
1826
- // the animation.
1827
- const DELAY_BETWEEN_ITEMS = (FULL_DURATION - ITEM_OPACITY_INITIAL_DELAY - ITEM_OPACITY_DURATION) / children.length;
1828
- // The mock has the animation shrink to 35%
1829
- const surfaceHeightAnimation = surfaceEl.animate([{
1830
- height: `${height}px`
1831
- }, {
1832
- height: `${height * END_HEIGHT_PERCENTAGE}px`
1833
- }], {
1834
- duration: FULL_DURATION,
1835
- easing: EASING.EMPHASIZED_ACCELERATE
1836
- });
1837
- // When we are closing downwards, we want to make sure the last item is
1838
- // always in view, so we need to translate it upwards the opposite direction
1839
- // of the height animation
1840
- const downPositionCorrectionAnimation = slotEl.animate([{
1841
- transform: ''
1842
- }, {
1843
- transform: closingDownwards ? `translateY(-${height * (1 - END_HEIGHT_PERCENTAGE)}px)` : ''
1844
- }], {
1845
- duration: FULL_DURATION,
1846
- easing: EASING.EMPHASIZED_ACCELERATE
1847
- });
1848
- const surfaceOpacityAnimation = surfaceEl.animate([{
1849
- opacity: 1
1850
- }, {
1851
- opacity: 0
1852
- }], {
1853
- duration: SURFACE_OPACITY_DURATION,
1854
- delay: SURFACE_OPACITY_DELAY
1855
- });
1856
- const childrenAnimations = [];
1857
- for (let i = 0; i < children.length; i++) {
1858
- // If the animation is closing upwards, then reverse the list of
1859
- // children so that we animate in the opposite direction.
1860
- const directionalIndex = closingDownwards ? i : children.length - 1 - i;
1861
- const child = children[directionalIndex];
1862
- const animation = child.animate([{
1863
- opacity: 1
1864
- }, {
1865
- opacity: 0
1866
- }], {
1867
- duration: ITEM_OPACITY_DURATION,
1868
- delay: ITEM_OPACITY_INITIAL_DELAY + DELAY_BETWEEN_ITEMS * i
1869
- });
1870
- // Make sure the items stay hidden at the end of each child animation.
1871
- // We clean this up at the end of the overall animation.
1872
- animation.addEventListener('finish', () => {
1873
- child.classList.toggle('md-menu-hidden', true);
1874
- });
1875
- childrenAnimations.push([child, animation]);
1876
- }
1877
- signal.addEventListener('abort', () => {
1878
- surfaceHeightAnimation.cancel();
1879
- downPositionCorrectionAnimation.cancel();
1880
- surfaceOpacityAnimation.cancel();
1881
- childrenAnimations.forEach(([child, animation]) => {
1882
- animation.cancel();
1883
- child.classList.toggle('md-menu-hidden', false);
1884
- });
1885
- resolve(false);
1886
- });
1887
- surfaceHeightAnimation.addEventListener('finish', () => {
1888
- surfaceEl.classList.toggle('animating', false);
1889
- childrenAnimations.forEach(([child]) => {
1890
- child.classList.toggle('md-menu-hidden', false);
1891
- });
1892
- this.openCloseAnimationSignal.finish();
1893
- this.dispatchEvent(new Event('closed'));
1894
- resolve(true);
1895
- });
1896
- return animationEnded;
1897
- }
1898
- handleKeydown(event) {
1899
- // At any key event, the pointer interaction is done so we need to clear our
1900
- // cached pointerpath. This handles the case where the user clicks on the
1901
- // anchor, and then hits shift+tab
1902
- this.pointerPath = [];
1903
- this.listController.handleKeydown(event);
1904
- }
1905
- setUpGlobalEventListeners() {
1906
- document.addEventListener('click', this.onDocumentClick, {
1907
- capture: true
1908
- });
1909
- window.addEventListener('pointerdown', this.onWindowPointerdown);
1910
- document.addEventListener('resize', this.onWindowResize, {
1911
- passive: true
1912
- });
1913
- window.addEventListener('resize', this.onWindowResize, {
1914
- passive: true
1915
- });
1916
- }
1917
- cleanUpGlobalEventListeners() {
1918
- document.removeEventListener('click', this.onDocumentClick, {
1919
- capture: true
1920
- });
1921
- window.removeEventListener('pointerdown', this.onWindowPointerdown);
1922
- document.removeEventListener('resize', this.onWindowResize);
1923
- window.removeEventListener('resize', this.onWindowResize);
1924
- }
1925
- onCloseMenu() {
1926
- this.close();
1927
- }
1928
- onDeactivateItems(event) {
1929
- event.stopPropagation();
1930
- this.listController.onDeactivateItems();
1931
- }
1932
- onRequestActivation(event) {
1933
- event.stopPropagation();
1934
- this.listController.onRequestActivation(event);
1935
- }
1936
- handleDeactivateTypeahead(event) {
1937
- // stopPropagation so that this does not deactivate any typeaheads in menus
1938
- // nested above it e.g. md-sub-menu
1939
- event.stopPropagation();
1940
- this.typeaheadActive = false;
1941
- }
1942
- handleActivateTypeahead(event) {
1943
- // stopPropagation so that this does not activate any typeaheads in menus
1944
- // nested above it e.g. md-sub-menu
1945
- event.stopPropagation();
1946
- this.typeaheadActive = true;
1947
- }
1948
- handleStayOpenOnFocusout(event) {
1949
- event.stopPropagation();
1950
- this.stayOpenOnFocusout = true;
1951
- }
1952
- handleCloseOnFocusout(event) {
1953
- event.stopPropagation();
1954
- this.stayOpenOnFocusout = false;
1955
- }
1956
- close() {
1957
- this.open = false;
1958
- const maybeSubmenu = this.slotItems;
1959
- maybeSubmenu.forEach(item => {
1960
- var _item$close;
1961
- (_item$close = item.close) === null || _item$close === void 0 || _item$close.call(item);
1962
- });
1963
- }
1964
- show() {
1965
- this.open = true;
1966
- }
1967
- /**
1968
- * Activates the next item in the menu. If at the end of the menu, the first
1969
- * item will be activated.
1970
- *
1971
- * @return The activated menu item or `null` if there are no items.
1972
- */
1973
- activateNextItem() {
1974
- return this.listController.activateNextItem() ?? null;
1975
- }
1976
- /**
1977
- * Activates the previous item in the menu. If at the start of the menu, the
1978
- * last item will be activated.
1979
- *
1980
- * @return The activated menu item or `null` if there are no items.
1981
- */
1982
- activatePreviousItem() {
1983
- return this.listController.activatePreviousItem() ?? null;
1984
- }
1985
- /**
1986
- * Repositions the menu if it is open.
1987
- *
1988
- * Useful for the case where document or window-positioned menus have their
1989
- * anchors moved while open.
1990
- */
1991
- reposition() {
1992
- if (this.open) {
1993
- this.menuPositionController.position();
1994
- }
1995
- }
1996
- }
1997
- __decorate([e$1('.menu')], Menu.prototype, "surfaceEl", void 0);
1998
- __decorate([e$1('slot')], Menu.prototype, "slotEl", void 0);
1999
- __decorate([n$1()], Menu.prototype, "anchor", void 0);
2000
- __decorate([n$1()], Menu.prototype, "positioning", void 0);
2001
- __decorate([n$1({
2002
- type: Boolean
2003
- })], Menu.prototype, "quick", void 0);
2004
- __decorate([n$1({
2005
- type: Boolean,
2006
- attribute: 'has-overflow'
2007
- })], Menu.prototype, "hasOverflow", void 0);
2008
- __decorate([n$1({
2009
- type: Boolean,
2010
- reflect: true
2011
- })], Menu.prototype, "open", void 0);
2012
- __decorate([n$1({
2013
- type: Number,
2014
- attribute: 'x-offset'
2015
- })], Menu.prototype, "xOffset", void 0);
2016
- __decorate([n$1({
2017
- type: Number,
2018
- attribute: 'y-offset'
2019
- })], Menu.prototype, "yOffset", void 0);
2020
- __decorate([n$1({
2021
- type: Boolean,
2022
- attribute: 'no-horizontal-flip'
2023
- })], Menu.prototype, "noHorizontalFlip", void 0);
2024
- __decorate([n$1({
2025
- type: Boolean,
2026
- attribute: 'no-vertical-flip'
2027
- })], Menu.prototype, "noVerticalFlip", void 0);
2028
- __decorate([n$1({
2029
- type: Number,
2030
- attribute: 'typeahead-delay'
2031
- })], Menu.prototype, "typeaheadDelay", void 0);
2032
- __decorate([n$1({
2033
- attribute: 'anchor-corner'
2034
- })], Menu.prototype, "anchorCorner", void 0);
2035
- __decorate([n$1({
2036
- attribute: 'menu-corner'
2037
- })], Menu.prototype, "menuCorner", void 0);
2038
- __decorate([n$1({
2039
- type: Boolean,
2040
- attribute: 'stay-open-on-outside-click'
2041
- })], Menu.prototype, "stayOpenOnOutsideClick", void 0);
2042
- __decorate([n$1({
2043
- type: Boolean,
2044
- attribute: 'stay-open-on-focusout'
2045
- })], Menu.prototype, "stayOpenOnFocusout", void 0);
2046
- __decorate([n$1({
2047
- type: Boolean,
2048
- attribute: 'skip-restore-focus'
2049
- })], Menu.prototype, "skipRestoreFocus", void 0);
2050
- __decorate([n$1({
2051
- attribute: 'default-focus'
2052
- })], Menu.prototype, "defaultFocus", void 0);
2053
- __decorate([n$1({
2054
- type: Boolean,
2055
- attribute: 'no-navigation-wrap'
2056
- })], Menu.prototype, "noNavigationWrap", void 0);
2057
- __decorate([o({
2058
- flatten: true
2059
- })], Menu.prototype, "slotItems", void 0);
2060
- __decorate([r()], Menu.prototype, "typeaheadActive", void 0);
2061
-
2062
- /**
2063
- * @license
2064
- * Copyright 2024 Google LLC
2065
- * SPDX-License-Identifier: Apache-2.0
2066
- */
2067
- // Generated stylesheet for ./menu/internal/menu-styles.css.
2068
- const styles$3 = i$1`:host{--md-elevation-level: var(--md-menu-container-elevation, 2);--md-elevation-shadow-color: var(--md-menu-container-shadow-color, var(--md-sys-color-shadow, #000));min-width:112px;color:unset;display:contents}md-focus-ring{--md-focus-ring-shape: var(--md-menu-container-shape, var(--md-sys-shape-corner-extra-small, 4px))}.menu{border-radius:var(--md-menu-container-shape, var(--md-sys-shape-corner-extra-small, 4px));display:none;inset:auto;border:none;padding:0px;overflow:visible;background-color:rgba(0,0,0,0);color:inherit;opacity:0;z-index:20;position:absolute;user-select:none;max-height:inherit;height:inherit;min-width:inherit;max-width:inherit;scrollbar-width:inherit}.menu::backdrop{display:none}.fixed{position:fixed}.items{display:block;list-style-type:none;margin:0;outline:none;box-sizing:border-box;background-color:var(--md-menu-container-color, var(--md-sys-color-surface-container, #f3edf7));height:inherit;max-height:inherit;overflow:auto;min-width:inherit;max-width:inherit;border-radius:inherit;scrollbar-width:inherit}.item-padding{padding-block:var(--md-menu-top-space, 8px) var(--md-menu-bottom-space, 8px)}.has-overflow:not([popover]) .items{overflow:visible}.has-overflow.animating .items,.animating .items{overflow:hidden}.has-overflow.animating .items{pointer-events:none}.animating ::slotted(.md-menu-hidden){opacity:0}slot{display:block;height:inherit;max-height:inherit}::slotted(:is(md-divider,[role=separator])){margin:8px 0}@media(forced-colors: active){.menu{border-style:solid;border-color:CanvasText;border-width:1px}}
2069
- `;
2070
-
2071
- /**
2072
- * @license
2073
- * Copyright 2022 Google LLC
2074
- * SPDX-License-Identifier: Apache-2.0
2075
- */
2076
- /**
2077
- * @summary Menus display a list of choices on a temporary surface.
2078
- *
2079
- * @description
2080
- * Menus appear when users interact with a button, action, or other control.
2081
- *
2082
- * They can be opened from a variety of elements, most commonly icon buttons,
2083
- * buttons, and text fields.
2084
- *
2085
- * md-menu listens for the `close-menu` and `deselect-items` events.
2086
- *
2087
- * - `close-menu` closes the menu when dispatched from a child element.
2088
- * - `deselect-items` deselects all of its immediate menu-item children.
2089
- *
2090
- * @example
2091
- * ```html
2092
- * <div style="position:relative;">
2093
- * <button
2094
- * id="anchor"
2095
- * @click=${() => this.menuRef.value.show()}>
2096
- * Click to open menu
2097
- * </button>
2098
- * <!--
2099
- * `has-overflow` is required when using a submenu which overflows the
2100
- * menu's contents.
2101
- *
2102
- * Additionally, `anchor` ingests an idref which do not pass through shadow
2103
- * roots. You can also set `.anchorElement` to an element reference if
2104
- * necessary.
2105
- * -->
2106
- * <md-menu anchor="anchor" has-overflow ${ref(menuRef)}>
2107
- * <md-menu-item headline="This is a headline"></md-menu-item>
2108
- * <md-sub-menu>
2109
- * <md-menu-item
2110
- * slot="item"
2111
- * headline="this is a submenu item">
2112
- * </md-menu-item>
2113
- * <md-menu slot="menu">
2114
- * <md-menu-item headline="This is an item inside a submenu">
2115
- * </md-menu-item>
2116
- * </md-menu>
2117
- * </md-sub-menu>
2118
- * </md-menu>
2119
- * </div>
2120
- * ```
2121
- *
2122
- * @final
2123
- * @suppress {visibility}
2124
- */
2125
- let MdMenu = class MdMenu extends Menu {};
2126
- MdMenu.styles = [styles$3];
2127
- MdMenu = __decorate([t('md-menu')], MdMenu);
2128
-
2129
- /**
2130
- * @license
2131
- * Copyright 2023 Google LLC
2132
- * SPDX-License-Identifier: Apache-2.0
2133
- */
2134
- /**
2135
- * A validator that provides constraint validation that emulates `<select>`
2136
- * validation.
2137
- */
2138
- class SelectValidator extends Validator {
2139
- computeValidity(state) {
2140
- if (!this.selectControl) {
2141
- // Lazily create the platform select
2142
- this.selectControl = document.createElement('select');
2143
- }
2144
- D(b`<option value=${state.value}></option>`, this.selectControl);
2145
- this.selectControl.value = state.value;
2146
- this.selectControl.required = state.required;
2147
- return {
2148
- validity: this.selectControl.validity,
2149
- validationMessage: this.selectControl.validationMessage
2150
- };
2151
- }
2152
- equals(prev, next) {
2153
- return prev.value === next.value && prev.required === next.required;
2154
- }
2155
- copy({
2156
- value,
2157
- required
2158
- }) {
2159
- return {
2160
- value,
2161
- required
2162
- };
2163
- }
2164
- }
2165
-
2166
- /**
2167
- * @license
2168
- * Copyright 2023 Google LLC
2169
- * SPDX-License-Identifier: Apache-2.0
2170
- */
2171
- /**
2172
- * Given a list of select options, this function will return an array of
2173
- * SelectOptionRecords that are selected.
2174
- *
2175
- * @return An array of SelectOptionRecords describing the options that are
2176
- * selected.
2177
- */
2178
- function getSelectedItems(items) {
2179
- const selectedItemRecords = [];
2180
- for (let i = 0; i < items.length; i++) {
2181
- const item = items[i];
2182
- if (item.selected) {
2183
- selectedItemRecords.push([item, i]);
2184
- }
2185
- }
2186
- return selectedItemRecords;
2187
- }
2188
-
2189
- /**
2190
- * @license
2191
- * Copyright 2023 Google LLC
2192
- * SPDX-License-Identifier: Apache-2.0
2193
- */
2194
- var _a;
2195
- const VALUE = Symbol('value');
2196
- // Separate variable needed for closure.
2197
- const selectBaseClass = mixinDelegatesAria(mixinOnReportValidity(mixinConstraintValidation(mixinFormAssociated(mixinElementInternals(i)))));
2198
- /**
2199
- * @fires change {Event} The native `change` event on
2200
- * [`<input>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event)
2201
- * --bubbles
2202
- * @fires input {InputEvent} The native `input` event on
2203
- * [`<input>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event)
2204
- * --bubbles --composed
2205
- * @fires opening {Event} Fired when the select's menu is about to open.
2206
- * @fires opened {Event} Fired when the select's menu has finished animations
2207
- * and opened.
2208
- * @fires closing {Event} Fired when the select's menu is about to close.
2209
- * @fires closed {Event} Fired when the select's menu has finished animations
2210
- * and closed.
2211
- */
2212
- class Select extends selectBaseClass {
2213
- /**
2214
- * The value of the currently selected option.
2215
- *
2216
- * Note: For SSR, set `[selected]` on the requested option and `displayText`
2217
- * rather than setting `value` setting `value` will incur a DOM query.
2218
- */
2219
- get value() {
2220
- return this[VALUE];
2221
- }
2222
- set value(value) {
2223
- this.lastUserSetValue = value;
2224
- this.select(value);
2225
- }
2226
- get options() {
2227
- var _this$menu;
2228
- // NOTE: this does a DOM query.
2229
- return ((_this$menu = this.menu) === null || _this$menu === void 0 ? void 0 : _this$menu.items) ?? [];
2230
- }
2231
- /**
2232
- * The index of the currently selected option.
2233
- *
2234
- * Note: For SSR, set `[selected]` on the requested option and `displayText`
2235
- * rather than setting `selectedIndex` setting `selectedIndex` will incur a
2236
- * DOM query.
2237
- */
2238
- get selectedIndex() {
2239
- // tslint:disable-next-line:enforce-name-casing
2240
- const [_option, index] = (this.getSelectedOptions() ?? [])[0] ?? [];
2241
- return index ?? -1;
2242
- }
2243
- set selectedIndex(index) {
2244
- this.lastUserSetSelectedIndex = index;
2245
- this.selectIndex(index);
2246
- }
2247
- /**
2248
- * Returns an array of selected options.
2249
- *
2250
- * NOTE: md-select only supports single selection.
2251
- */
2252
- get selectedOptions() {
2253
- return (this.getSelectedOptions() ?? []).map(([option]) => option);
2254
- }
2255
- get hasError() {
2256
- return this.error || this.nativeError;
2257
- }
2258
- constructor() {
2259
- super();
2260
- /**
2261
- * Opens the menu synchronously with no animation.
2262
- */
2263
- this.quick = false;
2264
- /**
2265
- * Whether or not the select is required.
2266
- */
2267
- this.required = false;
2268
- /**
2269
- * The error message that replaces supporting text when `error` is true. If
2270
- * `errorText` is an empty string, then the supporting text will continue to
2271
- * show.
2272
- *
2273
- * This error message overrides the error message displayed by
2274
- * `reportValidity()`.
2275
- */
2276
- this.errorText = '';
2277
- /**
2278
- * The floating label for the field.
2279
- */
2280
- this.label = '';
2281
- /**
2282
- * Disables the asterisk on the floating label, when the select is
2283
- * required.
2284
- */
2285
- this.noAsterisk = false;
2286
- /**
2287
- * Conveys additional information below the select, such as how it should
2288
- * be used.
2289
- */
2290
- this.supportingText = '';
2291
- /**
2292
- * Gets or sets whether or not the select is in a visually invalid state.
2293
- *
2294
- * This error state overrides the error state controlled by
2295
- * `reportValidity()`.
2296
- */
2297
- this.error = false;
2298
- /**
2299
- * Whether or not the underlying md-menu should be position: fixed to display
2300
- * in a top-level manner, or position: absolute.
2301
- *
2302
- * position:fixed is useful for cases where select is inside of another
2303
- * element with stacking context and hidden overflows such as `md-dialog`.
2304
- */
2305
- this.menuPositioning = 'popover';
2306
- /**
2307
- * Clamps the menu-width to the width of the select.
2308
- */
2309
- this.clampMenuWidth = false;
2310
- /**
2311
- * The max time between the keystrokes of the typeahead select / menu behavior
2312
- * before it clears the typeahead buffer.
2313
- */
2314
- this.typeaheadDelay = DEFAULT_TYPEAHEAD_BUFFER_TIME;
2315
- /**
2316
- * Whether or not the text field has a leading icon. Used for SSR.
2317
- */
2318
- this.hasLeadingIcon = false;
2319
- /**
2320
- * Text to display in the field. Only set for SSR.
2321
- */
2322
- this.displayText = '';
2323
- /**
2324
- * Whether the menu should be aligned to the start or the end of the select's
2325
- * textbox.
2326
- */
2327
- this.menuAlign = 'start';
2328
- this[_a] = '';
2329
- /**
2330
- * Used for initializing select when the user sets the `value` directly.
2331
- */
2332
- this.lastUserSetValue = null;
2333
- /**
2334
- * Used for initializing select when the user sets the `selectedIndex`
2335
- * directly.
2336
- */
2337
- this.lastUserSetSelectedIndex = null;
2338
- /**
2339
- * Used for `input` and `change` event change detection.
2340
- */
2341
- this.lastSelectedOption = null;
2342
- // tslint:disable-next-line:enforce-name-casing
2343
- this.lastSelectedOptionRecords = [];
2344
- /**
2345
- * Whether or not a native error has been reported via `reportValidity()`.
2346
- */
2347
- this.nativeError = false;
2348
- /**
2349
- * The validation message displayed from a native error via
2350
- * `reportValidity()`.
2351
- */
2352
- this.nativeErrorText = '';
2353
- this.focused = false;
2354
- this.open = false;
2355
- this.defaultFocus = FocusState.NONE;
2356
- // Have to keep track of previous open because it's state and private and thus
2357
- // cannot be tracked in PropertyValues<this> map.
2358
- this.prevOpen = this.open;
2359
- this.selectWidth = 0;
2360
- this.addEventListener('focus', this.handleFocus.bind(this));
2361
- this.addEventListener('blur', this.handleBlur.bind(this));
2362
- }
2363
- /**
2364
- * Selects an option given the value of the option, and updates MdSelect's
2365
- * value.
2366
- */
2367
- select(value) {
2368
- const optionToSelect = this.options.find(option => option.value === value);
2369
- if (optionToSelect) {
2370
- this.selectItem(optionToSelect);
2371
- }
2372
- }
2373
- /**
2374
- * Selects an option given the index of the option, and updates MdSelect's
2375
- * value.
2376
- */
2377
- selectIndex(index) {
2378
- const optionToSelect = this.options[index];
2379
- if (optionToSelect) {
2380
- this.selectItem(optionToSelect);
2381
- }
2382
- }
2383
- /**
2384
- * Reset the select to its default value.
2385
- */
2386
- reset() {
2387
- for (const option of this.options) {
2388
- option.selected = option.hasAttribute('selected');
2389
- }
2390
- this.updateValueAndDisplayText();
2391
- this.nativeError = false;
2392
- this.nativeErrorText = '';
2393
- }
2394
- /** Shows the picker. If it's already open, this is a no-op. */
2395
- showPicker() {
2396
- this.open = true;
2397
- }
2398
- [(_a = VALUE, onReportValidity)](invalidEvent) {
2399
- // Prevent default pop-up behavior.
2400
- invalidEvent === null || invalidEvent === void 0 || invalidEvent.preventDefault();
2401
- const prevMessage = this.getErrorText();
2402
- this.nativeError = !!invalidEvent;
2403
- this.nativeErrorText = this.validationMessage;
2404
- if (prevMessage === this.getErrorText()) {
2405
- var _this$field;
2406
- (_this$field = this.field) === null || _this$field === void 0 || _this$field.reannounceError();
2407
- }
2408
- }
2409
- update(changed) {
2410
- // In SSR the options will be ready to query, so try to figure out what
2411
- // the value and display text should be.
2412
- if (!this.hasUpdated) {
2413
- this.initUserSelection();
2414
- }
2415
- // We have just opened the menu.
2416
- // We are only able to check for the select's rect in `update()` instead of
2417
- // having to wait for `updated()` because the menu can never be open on
2418
- // first render since it is not settable and Lit SSR does not support click
2419
- // events which would open the menu.
2420
- if (this.prevOpen !== this.open && this.open) {
2421
- const selectRect = this.getBoundingClientRect();
2422
- this.selectWidth = selectRect.width;
2423
- }
2424
- this.prevOpen = this.open;
2425
- super.update(changed);
2426
- }
2427
- render() {
2428
- return b`
2429
- <span
2430
- class="select ${e$2(this.getRenderClasses())}"
2431
- @focusout=${this.handleFocusout}>
2432
- ${this.renderField()} ${this.renderMenu()}
2433
- </span>
2434
- `;
2435
- }
2436
- async firstUpdated(changed) {
2437
- var _this$menu2;
2438
- await ((_this$menu2 = this.menu) === null || _this$menu2 === void 0 ? void 0 : _this$menu2.updateComplete);
2439
- // If this has been handled on update already due to SSR, try again.
2440
- if (!this.lastSelectedOptionRecords.length) {
2441
- this.initUserSelection();
2442
- }
2443
- // Case for when the DOM is streaming, there are no children, and a child
2444
- // has [selected] set on it, we need to wait for DOM to render something.
2445
- if (!this.lastSelectedOptionRecords.length && true && !this.options.length) {
2446
- setTimeout(() => {
2447
- this.updateValueAndDisplayText();
2448
- });
2449
- }
2450
- super.firstUpdated(changed);
2451
- }
2452
- getRenderClasses() {
2453
- return {
2454
- 'disabled': this.disabled,
2455
- 'error': this.error,
2456
- 'open': this.open
2457
- };
2458
- }
2459
- renderField() {
2460
- const ariaLabel = this.ariaLabel || this.label;
2461
- return u`
2462
- <${this.fieldTag}
2463
- aria-haspopup="listbox"
2464
- role="combobox"
2465
- part="field"
2466
- id="field"
2467
- tabindex=${this.disabled ? '-1' : '0'}
2468
- aria-label=${ariaLabel || A}
2469
- aria-describedby="description"
2470
- aria-expanded=${this.open ? 'true' : 'false'}
2471
- aria-controls="listbox"
2472
- class="field"
2473
- label=${this.label}
2474
- ?no-asterisk=${this.noAsterisk}
2475
- .focused=${this.focused || this.open}
2476
- .populated=${!!this.displayText}
2477
- .disabled=${this.disabled}
2478
- .required=${this.required}
2479
- .error=${this.hasError}
2480
- ?has-start=${this.hasLeadingIcon}
2481
- has-end
2482
- supporting-text=${this.supportingText}
2483
- error-text=${this.getErrorText()}
2484
- @keydown=${this.handleKeydown}
2485
- @click=${this.handleClick}>
2486
- ${this.renderFieldContent()}
2487
- <div id="description" slot="aria-describedby"></div>
2488
- </${this.fieldTag}>`;
2489
- }
2490
- renderFieldContent() {
2491
- return [this.renderLeadingIcon(), this.renderLabel(), this.renderTrailingIcon()];
2492
- }
2493
- renderLeadingIcon() {
2494
- return b`
2495
- <span class="icon leading" slot="start">
2496
- <slot name="leading-icon" @slotchange=${this.handleIconChange}></slot>
2497
- </span>
2498
- `;
2499
- }
2500
- renderTrailingIcon() {
2501
- return b`
2502
- <span class="icon trailing" slot="end">
2503
- <slot name="trailing-icon" @slotchange=${this.handleIconChange}>
2504
- <svg height="5" viewBox="7 10 10 5" focusable="false">
2505
- <polygon
2506
- class="down"
2507
- stroke="none"
2508
- fill-rule="evenodd"
2509
- points="7 10 12 15 17 10"></polygon>
2510
- <polygon
2511
- class="up"
2512
- stroke="none"
2513
- fill-rule="evenodd"
2514
- points="7 15 12 10 17 15"></polygon>
2515
- </svg>
2516
- </slot>
2517
- </span>
2518
- `;
2519
- }
2520
- renderLabel() {
2521
- // need to render &nbsp; so that line-height can apply and give it a
2522
- // non-zero height
2523
- return b`<div id="label">${this.displayText || b`&nbsp;`}</div>`;
2524
- }
2525
- renderMenu() {
2526
- const ariaLabel = this.label || this.ariaLabel;
2527
- return b`<div class="menu-wrapper">
2528
- <md-menu
2529
- id="listbox"
2530
- .defaultFocus=${this.defaultFocus}
2531
- role="listbox"
2532
- tabindex="-1"
2533
- aria-label=${ariaLabel || A}
2534
- stay-open-on-focusout
2535
- part="menu"
2536
- exportparts="focus-ring: menu-focus-ring"
2537
- anchor="field"
2538
- style=${o$1({
2539
- '--__menu-min-width': `${this.selectWidth}px`,
2540
- '--__menu-max-width': this.clampMenuWidth ? `${this.selectWidth}px` : undefined
2541
- })}
2542
- no-navigation-wrap
2543
- .open=${this.open}
2544
- .quick=${this.quick}
2545
- .positioning=${this.menuPositioning}
2546
- .typeaheadDelay=${this.typeaheadDelay}
2547
- .anchorCorner=${this.menuAlign === 'start' ? 'end-start' : 'end-end'}
2548
- .menuCorner=${this.menuAlign === 'start' ? 'start-start' : 'start-end'}
2549
- @opening=${this.handleOpening}
2550
- @opened=${this.redispatchEvent}
2551
- @closing=${this.redispatchEvent}
2552
- @closed=${this.handleClosed}
2553
- @close-menu=${this.handleCloseMenu}
2554
- @request-selection=${this.handleRequestSelection}
2555
- @request-deselection=${this.handleRequestDeselection}>
2556
- ${this.renderMenuContent()}
2557
- </md-menu>
2558
- </div>`;
2559
- }
2560
- renderMenuContent() {
2561
- return b`<slot></slot>`;
2562
- }
2563
- /**
2564
- * Handles opening the select on keydown and typahead selection when the menu
2565
- * is closed.
2566
- */
2567
- handleKeydown(event) {
2568
- if (this.open || this.disabled || !this.menu) {
2569
- return;
2570
- }
2571
- const typeaheadController = this.menu.typeaheadController;
2572
- const isOpenKey = event.code === 'Space' || event.code === 'ArrowDown' || event.code === 'ArrowUp' || event.code === 'End' || event.code === 'Home' || event.code === 'Enter';
2573
- // Do not open if currently typing ahead because the user may be typing the
2574
- // spacebar to match a word with a space
2575
- if (!typeaheadController.isTypingAhead && isOpenKey) {
2576
- event.preventDefault();
2577
- this.open = true;
2578
- // https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/#kbd_label
2579
- switch (event.code) {
2580
- case 'Space':
2581
- case 'ArrowDown':
2582
- case 'Enter':
2583
- // We will handle focusing last selected item in this.handleOpening()
2584
- this.defaultFocus = FocusState.NONE;
2585
- break;
2586
- case 'End':
2587
- this.defaultFocus = FocusState.LAST_ITEM;
2588
- break;
2589
- case 'ArrowUp':
2590
- case 'Home':
2591
- this.defaultFocus = FocusState.FIRST_ITEM;
2592
- break;
2593
- }
2594
- return;
2595
- }
2596
- const isPrintableKey = event.key.length === 1;
2597
- // Handles typing ahead when the menu is closed by delegating the event to
2598
- // the underlying menu's typeaheadController
2599
- if (isPrintableKey) {
2600
- var _this$labelEl, _this$labelEl$setAttr;
2601
- typeaheadController.onKeydown(event);
2602
- event.preventDefault();
2603
- const {
2604
- lastActiveRecord
2605
- } = typeaheadController;
2606
- if (!lastActiveRecord) {
2607
- return;
2608
- }
2609
- (_this$labelEl = this.labelEl) === null || _this$labelEl === void 0 || (_this$labelEl$setAttr = _this$labelEl.setAttribute) === null || _this$labelEl$setAttr === void 0 || _this$labelEl$setAttr.call(_this$labelEl, 'aria-live', 'polite');
2610
- const hasChanged = this.selectItem(lastActiveRecord[TYPEAHEAD_RECORD.ITEM]);
2611
- if (hasChanged) {
2612
- this.dispatchInteractionEvents();
2613
- }
2614
- }
2615
- }
2616
- handleClick() {
2617
- this.open = !this.open;
2618
- }
2619
- handleFocus() {
2620
- this.focused = true;
2621
- }
2622
- handleBlur() {
2623
- this.focused = false;
2624
- }
2625
- /**
2626
- * Handles closing the menu when the focus leaves the select's subtree.
2627
- */
2628
- handleFocusout(event) {
2629
- // Don't close the menu if we are switching focus between menu,
2630
- // select-option, and field
2631
- if (event.relatedTarget && isElementInSubtree(event.relatedTarget, this)) {
2632
- return;
2633
- }
2634
- this.open = false;
2635
- }
2636
- /**
2637
- * Gets a list of all selected select options as a list item record array.
2638
- *
2639
- * @return An array of selected list option records.
2640
- */
2641
- getSelectedOptions() {
2642
- if (!this.menu) {
2643
- this.lastSelectedOptionRecords = [];
2644
- return null;
2645
- }
2646
- const items = this.menu.items;
2647
- this.lastSelectedOptionRecords = getSelectedItems(items);
2648
- return this.lastSelectedOptionRecords;
2649
- }
2650
- async getUpdateComplete() {
2651
- var _this$menu3;
2652
- await ((_this$menu3 = this.menu) === null || _this$menu3 === void 0 ? void 0 : _this$menu3.updateComplete);
2653
- return super.getUpdateComplete();
2654
- }
2655
- /**
2656
- * Gets the selected options from the DOM, and updates the value and display
2657
- * text to the first selected option's value and headline respectively.
2658
- *
2659
- * @return Whether or not the selected option has changed since last update.
2660
- */
2661
- updateValueAndDisplayText() {
2662
- const selectedOptions = this.getSelectedOptions() ?? [];
2663
- // Used to determine whether or not we need to fire an input / change event
2664
- // which fire whenever the option element changes (value or selectedIndex)
2665
- // on user interaction.
2666
- let hasSelectedOptionChanged = false;
2667
- if (selectedOptions.length) {
2668
- const [firstSelectedOption] = selectedOptions[0];
2669
- hasSelectedOptionChanged = this.lastSelectedOption !== firstSelectedOption;
2670
- this.lastSelectedOption = firstSelectedOption;
2671
- this[VALUE] = firstSelectedOption.value;
2672
- this.displayText = firstSelectedOption.displayText;
2673
- } else {
2674
- hasSelectedOptionChanged = this.lastSelectedOption !== null;
2675
- this.lastSelectedOption = null;
2676
- this[VALUE] = '';
2677
- this.displayText = '';
2678
- }
2679
- return hasSelectedOptionChanged;
2680
- }
2681
- /**
2682
- * Focuses and activates the last selected item upon opening, and resets other
2683
- * active items.
2684
- */
2685
- async handleOpening(e) {
2686
- var _this$labelEl2, _this$labelEl2$remove, _getActiveItem;
2687
- (_this$labelEl2 = this.labelEl) === null || _this$labelEl2 === void 0 || (_this$labelEl2$remove = _this$labelEl2.removeAttribute) === null || _this$labelEl2$remove === void 0 || _this$labelEl2$remove.call(_this$labelEl2, 'aria-live');
2688
- this.redispatchEvent(e);
2689
- // FocusState.NONE means we want to handle focus ourselves and focus the
2690
- // last selected item.
2691
- if (this.defaultFocus !== FocusState.NONE) {
2692
- return;
2693
- }
2694
- const items = this.menu.items;
2695
- const activeItem = (_getActiveItem = getActiveItem(items)) === null || _getActiveItem === void 0 ? void 0 : _getActiveItem.item;
2696
- let [selectedItem] = this.lastSelectedOptionRecords[0] ?? [null];
2697
- // This is true if the user keys through the list but clicks out of the menu
2698
- // thus no close-menu event is fired by an item and we can't clean up in
2699
- // handleCloseMenu.
2700
- if (activeItem && activeItem !== selectedItem) {
2701
- activeItem.tabIndex = -1;
2702
- }
2703
- // in the case that nothing is selected, focus the first item
2704
- selectedItem = selectedItem ?? items[0];
2705
- if (selectedItem) {
2706
- selectedItem.tabIndex = 0;
2707
- selectedItem.focus();
2708
- }
2709
- }
2710
- redispatchEvent(e) {
2711
- redispatchEvent(this, e);
2712
- }
2713
- handleClosed(e) {
2714
- this.open = false;
2715
- this.redispatchEvent(e);
2716
- }
2717
- /**
2718
- * Determines the reason for closing, and updates the UI accordingly.
2719
- */
2720
- handleCloseMenu(event) {
2721
- const reason = event.detail.reason;
2722
- const item = event.detail.itemPath[0];
2723
- this.open = false;
2724
- let hasChanged = false;
2725
- if (reason.kind === 'click-selection') {
2726
- hasChanged = this.selectItem(item);
2727
- } else if (reason.kind === 'keydown' && isSelectableKey(reason.key)) {
2728
- hasChanged = this.selectItem(item);
2729
- } else {
2730
- // This can happen on ESC being pressed
2731
- item.tabIndex = -1;
2732
- item.blur();
2733
- }
2734
- // Dispatch interaction events since selection has been made via keyboard
2735
- // or mouse.
2736
- if (hasChanged) {
2737
- this.dispatchInteractionEvents();
2738
- }
2739
- }
2740
- /**
2741
- * Selects a given option, deselects other options, and updates the UI.
2742
- *
2743
- * @return Whether the last selected option has changed.
2744
- */
2745
- selectItem(item) {
2746
- const selectedOptions = this.getSelectedOptions() ?? [];
2747
- selectedOptions.forEach(([option]) => {
2748
- if (item !== option) {
2749
- option.selected = false;
2750
- }
2751
- });
2752
- item.selected = true;
2753
- return this.updateValueAndDisplayText();
2754
- }
2755
- /**
2756
- * Handles updating selection when an option element requests selection via
2757
- * property / attribute change.
2758
- */
2759
- handleRequestSelection(event) {
2760
- const requestingOptionEl = event.target;
2761
- // No-op if this item is already selected.
2762
- if (this.lastSelectedOptionRecords.some(([option]) => option === requestingOptionEl)) {
2763
- return;
2764
- }
2765
- this.selectItem(requestingOptionEl);
2766
- }
2767
- /**
2768
- * Handles updating selection when an option element requests deselection via
2769
- * property / attribute change.
2770
- */
2771
- handleRequestDeselection(event) {
2772
- const requestingOptionEl = event.target;
2773
- // No-op if this item is not even in the list of tracked selected items.
2774
- if (!this.lastSelectedOptionRecords.some(([option]) => option === requestingOptionEl)) {
2775
- return;
2776
- }
2777
- this.updateValueAndDisplayText();
2778
- }
2779
- /**
2780
- * Attempts to initialize the selected option from user-settable values like
2781
- * SSR, setting `value`, or `selectedIndex` at startup.
2782
- */
2783
- initUserSelection() {
2784
- // User has set `.value` directly, but internals have not yet booted up.
2785
- if (this.lastUserSetValue && !this.lastSelectedOptionRecords.length) {
2786
- this.select(this.lastUserSetValue);
2787
- // User has set `.selectedIndex` directly, but internals have not yet
2788
- // booted up.
2789
- } else if (this.lastUserSetSelectedIndex !== null && !this.lastSelectedOptionRecords.length) {
2790
- this.selectIndex(this.lastUserSetSelectedIndex);
2791
- // Regular boot up!
2792
- } else {
2793
- this.updateValueAndDisplayText();
2794
- }
2795
- }
2796
- handleIconChange() {
2797
- this.hasLeadingIcon = this.leadingIcons.length > 0;
2798
- }
2799
- /**
2800
- * Dispatches the `input` and `change` events.
2801
- */
2802
- dispatchInteractionEvents() {
2803
- this.dispatchEvent(new Event('input', {
2804
- bubbles: true,
2805
- composed: true
2806
- }));
2807
- this.dispatchEvent(new Event('change', {
2808
- bubbles: true
2809
- }));
2810
- }
2811
- getErrorText() {
2812
- return this.error ? this.errorText : this.nativeErrorText;
2813
- }
2814
- [getFormValue]() {
2815
- return this.value;
2816
- }
2817
- formResetCallback() {
2818
- this.reset();
2819
- }
2820
- formStateRestoreCallback(state) {
2821
- this.value = state;
2822
- }
2823
- click() {
2824
- var _this$field2;
2825
- (_this$field2 = this.field) === null || _this$field2 === void 0 || _this$field2.click();
2826
- }
2827
- [createValidator]() {
2828
- return new SelectValidator(() => this);
2829
- }
2830
- [getValidityAnchor]() {
2831
- return this.field;
2832
- }
2833
- }
2834
- /** @nocollapse */
2835
- Select.shadowRootOptions = {
2836
- ...i.shadowRootOptions,
2837
- delegatesFocus: true
2838
- };
2839
- __decorate([n$1({
2840
- type: Boolean
2841
- })], Select.prototype, "quick", void 0);
2842
- __decorate([n$1({
2843
- type: Boolean
2844
- })], Select.prototype, "required", void 0);
2845
- __decorate([n$1({
2846
- type: String,
2847
- attribute: 'error-text'
2848
- })], Select.prototype, "errorText", void 0);
2849
- __decorate([n$1()], Select.prototype, "label", void 0);
2850
- __decorate([n$1({
2851
- type: Boolean,
2852
- attribute: 'no-asterisk'
2853
- })], Select.prototype, "noAsterisk", void 0);
2854
- __decorate([n$1({
2855
- type: String,
2856
- attribute: 'supporting-text'
2857
- })], Select.prototype, "supportingText", void 0);
2858
- __decorate([n$1({
2859
- type: Boolean,
2860
- reflect: true
2861
- })], Select.prototype, "error", void 0);
2862
- __decorate([n$1({
2863
- attribute: 'menu-positioning'
2864
- })], Select.prototype, "menuPositioning", void 0);
2865
- __decorate([n$1({
2866
- type: Boolean,
2867
- attribute: 'clamp-menu-width'
2868
- })], Select.prototype, "clampMenuWidth", void 0);
2869
- __decorate([n$1({
2870
- type: Number,
2871
- attribute: 'typeahead-delay'
2872
- })], Select.prototype, "typeaheadDelay", void 0);
2873
- __decorate([n$1({
2874
- type: Boolean,
2875
- attribute: 'has-leading-icon'
2876
- })], Select.prototype, "hasLeadingIcon", void 0);
2877
- __decorate([n$1({
2878
- attribute: 'display-text'
2879
- })], Select.prototype, "displayText", void 0);
2880
- __decorate([n$1({
2881
- attribute: 'menu-align'
2882
- })], Select.prototype, "menuAlign", void 0);
2883
- __decorate([n$1()], Select.prototype, "value", null);
2884
- __decorate([n$1({
2885
- type: Number,
2886
- attribute: 'selected-index'
2887
- })], Select.prototype, "selectedIndex", null);
2888
- __decorate([r()], Select.prototype, "nativeError", void 0);
2889
- __decorate([r()], Select.prototype, "nativeErrorText", void 0);
2890
- __decorate([r()], Select.prototype, "focused", void 0);
2891
- __decorate([r()], Select.prototype, "open", void 0);
2892
- __decorate([r()], Select.prototype, "defaultFocus", void 0);
2893
- __decorate([e$1('.field')], Select.prototype, "field", void 0);
2894
- __decorate([e$1('md-menu')], Select.prototype, "menu", void 0);
2895
- __decorate([e$1('#label')], Select.prototype, "labelEl", void 0);
2896
- __decorate([o({
2897
- slot: 'leading-icon',
2898
- flatten: true
2899
- })], Select.prototype, "leadingIcons", void 0);
2900
-
2901
- /**
2902
- * @license
2903
- * Copyright 2023 Google LLC
2904
- * SPDX-License-Identifier: Apache-2.0
2905
- */
2906
- // tslint:disable-next-line:enforce-comments-on-exported-symbols
2907
- class OutlinedSelect extends Select {
2908
- constructor() {
2909
- super(...arguments);
2910
- this.fieldTag = i$2`md-outlined-field`;
2911
- }
2912
- }
2913
-
2914
- /**
2915
- * @license
2916
- * Copyright 2024 Google LLC
2917
- * SPDX-License-Identifier: Apache-2.0
2918
- */
2919
- // Generated stylesheet for ./select/internal/outlined-select-styles.css.
2920
- const styles$2 = i$1`:host{--_text-field-disabled-input-text-color: var(--md-outlined-select-text-field-disabled-input-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-disabled-input-text-opacity: var(--md-outlined-select-text-field-disabled-input-text-opacity, 0.38);--_text-field-disabled-label-text-color: var(--md-outlined-select-text-field-disabled-label-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-disabled-label-text-opacity: var(--md-outlined-select-text-field-disabled-label-text-opacity, 0.38);--_text-field-disabled-leading-icon-color: var(--md-outlined-select-text-field-disabled-leading-icon-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-disabled-leading-icon-opacity: var(--md-outlined-select-text-field-disabled-leading-icon-opacity, 0.38);--_text-field-disabled-outline-color: var(--md-outlined-select-text-field-disabled-outline-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-disabled-outline-opacity: var(--md-outlined-select-text-field-disabled-outline-opacity, 0.12);--_text-field-disabled-outline-width: var(--md-outlined-select-text-field-disabled-outline-width, 1px);--_text-field-disabled-supporting-text-color: var(--md-outlined-select-text-field-disabled-supporting-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-disabled-supporting-text-opacity: var(--md-outlined-select-text-field-disabled-supporting-text-opacity, 0.38);--_text-field-disabled-trailing-icon-color: var(--md-outlined-select-text-field-disabled-trailing-icon-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-disabled-trailing-icon-opacity: var(--md-outlined-select-text-field-disabled-trailing-icon-opacity, 0.38);--_text-field-error-focus-input-text-color: var(--md-outlined-select-text-field-error-focus-input-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-error-focus-label-text-color: var(--md-outlined-select-text-field-error-focus-label-text-color, var(--md-sys-color-error, #b3261e));--_text-field-error-focus-leading-icon-color: var(--md-outlined-select-text-field-error-focus-leading-icon-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-error-focus-outline-color: var(--md-outlined-select-text-field-error-focus-outline-color, var(--md-sys-color-error, #b3261e));--_text-field-error-focus-supporting-text-color: var(--md-outlined-select-text-field-error-focus-supporting-text-color, var(--md-sys-color-error, #b3261e));--_text-field-error-focus-trailing-icon-color: var(--md-outlined-select-text-field-error-focus-trailing-icon-color, var(--md-sys-color-error, #b3261e));--_text-field-error-hover-input-text-color: var(--md-outlined-select-text-field-error-hover-input-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-error-hover-label-text-color: var(--md-outlined-select-text-field-error-hover-label-text-color, var(--md-sys-color-on-error-container, #410e0b));--_text-field-error-hover-leading-icon-color: var(--md-outlined-select-text-field-error-hover-leading-icon-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-error-hover-outline-color: var(--md-outlined-select-text-field-error-hover-outline-color, var(--md-sys-color-on-error-container, #410e0b));--_text-field-error-hover-supporting-text-color: var(--md-outlined-select-text-field-error-hover-supporting-text-color, var(--md-sys-color-error, #b3261e));--_text-field-error-hover-trailing-icon-color: var(--md-outlined-select-text-field-error-hover-trailing-icon-color, var(--md-sys-color-on-error-container, #410e0b));--_text-field-error-input-text-color: var(--md-outlined-select-text-field-error-input-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-error-label-text-color: var(--md-outlined-select-text-field-error-label-text-color, var(--md-sys-color-error, #b3261e));--_text-field-error-leading-icon-color: var(--md-outlined-select-text-field-error-leading-icon-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-error-outline-color: var(--md-outlined-select-text-field-error-outline-color, var(--md-sys-color-error, #b3261e));--_text-field-error-supporting-text-color: var(--md-outlined-select-text-field-error-supporting-text-color, var(--md-sys-color-error, #b3261e));--_text-field-error-trailing-icon-color: var(--md-outlined-select-text-field-error-trailing-icon-color, var(--md-sys-color-error, #b3261e));--_text-field-focus-input-text-color: var(--md-outlined-select-text-field-focus-input-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-focus-label-text-color: var(--md-outlined-select-text-field-focus-label-text-color, var(--md-sys-color-primary, #6750a4));--_text-field-focus-leading-icon-color: var(--md-outlined-select-text-field-focus-leading-icon-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-focus-outline-color: var(--md-outlined-select-text-field-focus-outline-color, var(--md-sys-color-primary, #6750a4));--_text-field-focus-outline-width: var(--md-outlined-select-text-field-focus-outline-width, 3px);--_text-field-focus-supporting-text-color: var(--md-outlined-select-text-field-focus-supporting-text-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-focus-trailing-icon-color: var(--md-outlined-select-text-field-focus-trailing-icon-color, var(--md-sys-color-primary, #6750a4));--_text-field-hover-input-text-color: var(--md-outlined-select-text-field-hover-input-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-hover-label-text-color: var(--md-outlined-select-text-field-hover-label-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-hover-leading-icon-color: var(--md-outlined-select-text-field-hover-leading-icon-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-hover-outline-color: var(--md-outlined-select-text-field-hover-outline-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-hover-outline-width: var(--md-outlined-select-text-field-hover-outline-width, 1px);--_text-field-hover-supporting-text-color: var(--md-outlined-select-text-field-hover-supporting-text-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-hover-trailing-icon-color: var(--md-outlined-select-text-field-hover-trailing-icon-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-input-text-color: var(--md-outlined-select-text-field-input-text-color, var(--md-sys-color-on-surface, #1d1b20));--_text-field-input-text-font: var(--md-outlined-select-text-field-input-text-font, var(--md-sys-typescale-body-large-font, var(--md-ref-typeface-plain, Roboto)));--_text-field-input-text-line-height: var(--md-outlined-select-text-field-input-text-line-height, var(--md-sys-typescale-body-large-line-height, 1.5rem));--_text-field-input-text-size: var(--md-outlined-select-text-field-input-text-size, var(--md-sys-typescale-body-large-size, 1rem));--_text-field-input-text-weight: var(--md-outlined-select-text-field-input-text-weight, var(--md-sys-typescale-body-large-weight, var(--md-ref-typeface-weight-regular, 400)));--_text-field-label-text-color: var(--md-outlined-select-text-field-label-text-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-label-text-font: var(--md-outlined-select-text-field-label-text-font, var(--md-sys-typescale-body-large-font, var(--md-ref-typeface-plain, Roboto)));--_text-field-label-text-line-height: var(--md-outlined-select-text-field-label-text-line-height, var(--md-sys-typescale-body-large-line-height, 1.5rem));--_text-field-label-text-populated-line-height: var(--md-outlined-select-text-field-label-text-populated-line-height, var(--md-sys-typescale-body-small-line-height, 1rem));--_text-field-label-text-populated-size: var(--md-outlined-select-text-field-label-text-populated-size, var(--md-sys-typescale-body-small-size, 0.75rem));--_text-field-label-text-size: var(--md-outlined-select-text-field-label-text-size, var(--md-sys-typescale-body-large-size, 1rem));--_text-field-label-text-weight: var(--md-outlined-select-text-field-label-text-weight, var(--md-sys-typescale-body-large-weight, var(--md-ref-typeface-weight-regular, 400)));--_text-field-leading-icon-color: var(--md-outlined-select-text-field-leading-icon-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-leading-icon-size: var(--md-outlined-select-text-field-leading-icon-size, 24px);--_text-field-outline-color: var(--md-outlined-select-text-field-outline-color, var(--md-sys-color-outline, #79747e));--_text-field-outline-width: var(--md-outlined-select-text-field-outline-width, 1px);--_text-field-supporting-text-color: var(--md-outlined-select-text-field-supporting-text-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-supporting-text-font: var(--md-outlined-select-text-field-supporting-text-font, var(--md-sys-typescale-body-small-font, var(--md-ref-typeface-plain, Roboto)));--_text-field-supporting-text-line-height: var(--md-outlined-select-text-field-supporting-text-line-height, var(--md-sys-typescale-body-small-line-height, 1rem));--_text-field-supporting-text-size: var(--md-outlined-select-text-field-supporting-text-size, var(--md-sys-typescale-body-small-size, 0.75rem));--_text-field-supporting-text-weight: var(--md-outlined-select-text-field-supporting-text-weight, var(--md-sys-typescale-body-small-weight, var(--md-ref-typeface-weight-regular, 400)));--_text-field-trailing-icon-color: var(--md-outlined-select-text-field-trailing-icon-color, var(--md-sys-color-on-surface-variant, #49454f));--_text-field-trailing-icon-size: var(--md-outlined-select-text-field-trailing-icon-size, 24px);--_text-field-container-shape-start-start: var(--md-outlined-select-text-field-container-shape-start-start, var(--md-outlined-select-text-field-container-shape, var(--md-sys-shape-corner-extra-small, 4px)));--_text-field-container-shape-start-end: var(--md-outlined-select-text-field-container-shape-start-end, var(--md-outlined-select-text-field-container-shape, var(--md-sys-shape-corner-extra-small, 4px)));--_text-field-container-shape-end-end: var(--md-outlined-select-text-field-container-shape-end-end, var(--md-outlined-select-text-field-container-shape, var(--md-sys-shape-corner-extra-small, 4px)));--_text-field-container-shape-end-start: var(--md-outlined-select-text-field-container-shape-end-start, var(--md-outlined-select-text-field-container-shape, var(--md-sys-shape-corner-extra-small, 4px)));--md-outlined-field-container-shape-end-end: var(--_text-field-container-shape-end-end);--md-outlined-field-container-shape-end-start: var(--_text-field-container-shape-end-start);--md-outlined-field-container-shape-start-end: var(--_text-field-container-shape-start-end);--md-outlined-field-container-shape-start-start: var(--_text-field-container-shape-start-start);--md-outlined-field-content-color: var(--_text-field-input-text-color);--md-outlined-field-content-font: var(--_text-field-input-text-font);--md-outlined-field-content-line-height: var(--_text-field-input-text-line-height);--md-outlined-field-content-size: var(--_text-field-input-text-size);--md-outlined-field-content-weight: var(--_text-field-input-text-weight);--md-outlined-field-disabled-content-color: var(--_text-field-disabled-input-text-color);--md-outlined-field-disabled-content-opacity: var(--_text-field-disabled-input-text-opacity);--md-outlined-field-disabled-label-text-color: var(--_text-field-disabled-label-text-color);--md-outlined-field-disabled-label-text-opacity: var(--_text-field-disabled-label-text-opacity);--md-outlined-field-disabled-leading-content-color: var(--_text-field-disabled-leading-icon-color);--md-outlined-field-disabled-leading-content-opacity: var(--_text-field-disabled-leading-icon-opacity);--md-outlined-field-disabled-outline-color: var(--_text-field-disabled-outline-color);--md-outlined-field-disabled-outline-opacity: var(--_text-field-disabled-outline-opacity);--md-outlined-field-disabled-outline-width: var(--_text-field-disabled-outline-width);--md-outlined-field-disabled-supporting-text-color: var(--_text-field-disabled-supporting-text-color);--md-outlined-field-disabled-supporting-text-opacity: var(--_text-field-disabled-supporting-text-opacity);--md-outlined-field-disabled-trailing-content-color: var(--_text-field-disabled-trailing-icon-color);--md-outlined-field-disabled-trailing-content-opacity: var(--_text-field-disabled-trailing-icon-opacity);--md-outlined-field-error-content-color: var(--_text-field-error-input-text-color);--md-outlined-field-error-focus-content-color: var(--_text-field-error-focus-input-text-color);--md-outlined-field-error-focus-label-text-color: var(--_text-field-error-focus-label-text-color);--md-outlined-field-error-focus-leading-content-color: var(--_text-field-error-focus-leading-icon-color);--md-outlined-field-error-focus-outline-color: var(--_text-field-error-focus-outline-color);--md-outlined-field-error-focus-supporting-text-color: var(--_text-field-error-focus-supporting-text-color);--md-outlined-field-error-focus-trailing-content-color: var(--_text-field-error-focus-trailing-icon-color);--md-outlined-field-error-hover-content-color: var(--_text-field-error-hover-input-text-color);--md-outlined-field-error-hover-label-text-color: var(--_text-field-error-hover-label-text-color);--md-outlined-field-error-hover-leading-content-color: var(--_text-field-error-hover-leading-icon-color);--md-outlined-field-error-hover-outline-color: var(--_text-field-error-hover-outline-color);--md-outlined-field-error-hover-supporting-text-color: var(--_text-field-error-hover-supporting-text-color);--md-outlined-field-error-hover-trailing-content-color: var(--_text-field-error-hover-trailing-icon-color);--md-outlined-field-error-label-text-color: var(--_text-field-error-label-text-color);--md-outlined-field-error-leading-content-color: var(--_text-field-error-leading-icon-color);--md-outlined-field-error-outline-color: var(--_text-field-error-outline-color);--md-outlined-field-error-supporting-text-color: var(--_text-field-error-supporting-text-color);--md-outlined-field-error-trailing-content-color: var(--_text-field-error-trailing-icon-color);--md-outlined-field-focus-content-color: var(--_text-field-focus-input-text-color);--md-outlined-field-focus-label-text-color: var(--_text-field-focus-label-text-color);--md-outlined-field-focus-leading-content-color: var(--_text-field-focus-leading-icon-color);--md-outlined-field-focus-outline-color: var(--_text-field-focus-outline-color);--md-outlined-field-focus-outline-width: var(--_text-field-focus-outline-width);--md-outlined-field-focus-supporting-text-color: var(--_text-field-focus-supporting-text-color);--md-outlined-field-focus-trailing-content-color: var(--_text-field-focus-trailing-icon-color);--md-outlined-field-hover-content-color: var(--_text-field-hover-input-text-color);--md-outlined-field-hover-label-text-color: var(--_text-field-hover-label-text-color);--md-outlined-field-hover-leading-content-color: var(--_text-field-hover-leading-icon-color);--md-outlined-field-hover-outline-color: var(--_text-field-hover-outline-color);--md-outlined-field-hover-outline-width: var(--_text-field-hover-outline-width);--md-outlined-field-hover-supporting-text-color: var(--_text-field-hover-supporting-text-color);--md-outlined-field-hover-trailing-content-color: var(--_text-field-hover-trailing-icon-color);--md-outlined-field-label-text-color: var(--_text-field-label-text-color);--md-outlined-field-label-text-font: var(--_text-field-label-text-font);--md-outlined-field-label-text-line-height: var(--_text-field-label-text-line-height);--md-outlined-field-label-text-populated-line-height: var(--_text-field-label-text-populated-line-height);--md-outlined-field-label-text-populated-size: var(--_text-field-label-text-populated-size);--md-outlined-field-label-text-size: var(--_text-field-label-text-size);--md-outlined-field-label-text-weight: var(--_text-field-label-text-weight);--md-outlined-field-leading-content-color: var(--_text-field-leading-icon-color);--md-outlined-field-outline-color: var(--_text-field-outline-color);--md-outlined-field-outline-width: var(--_text-field-outline-width);--md-outlined-field-supporting-text-color: var(--_text-field-supporting-text-color);--md-outlined-field-supporting-text-font: var(--_text-field-supporting-text-font);--md-outlined-field-supporting-text-line-height: var(--_text-field-supporting-text-line-height);--md-outlined-field-supporting-text-size: var(--_text-field-supporting-text-size);--md-outlined-field-supporting-text-weight: var(--_text-field-supporting-text-weight);--md-outlined-field-trailing-content-color: var(--_text-field-trailing-icon-color)}[has-start] .icon.leading{font-size:var(--_text-field-leading-icon-size);height:var(--_text-field-leading-icon-size);width:var(--_text-field-leading-icon-size)}.icon.trailing{font-size:var(--_text-field-trailing-icon-size);height:var(--_text-field-trailing-icon-size);width:var(--_text-field-trailing-icon-size)}
2921
- `;
2922
-
2923
- /**
2924
- * @license
2925
- * Copyright 2024 Google LLC
2926
- * SPDX-License-Identifier: Apache-2.0
2927
- */
2928
- // Generated stylesheet for ./select/internal/shared-styles.css.
2929
- const styles$1 = i$1`:host{color:unset;min-width:210px;display:flex}.field{cursor:default;outline:none}.select{position:relative;flex-direction:column}.icon.trailing svg,.icon ::slotted(*){fill:currentColor}.icon ::slotted(*){width:inherit;height:inherit;font-size:inherit}.icon slot{display:flex;height:100%;width:100%;align-items:center;justify-content:center}.icon.trailing :is(.up,.down){opacity:0;transition:opacity 75ms linear 75ms}.select:not(.open) .down,.select.open .up{opacity:1}.field,.select,md-menu{min-width:inherit;width:inherit;max-width:inherit;display:flex}md-menu{min-width:var(--__menu-min-width);max-width:var(--__menu-max-width, inherit)}.menu-wrapper{width:0px;height:0px;max-width:inherit}md-menu ::slotted(:not[disabled]){cursor:pointer}.field,.select{width:100%}:host{display:inline-flex}:host([disabled]){pointer-events:none}
2930
- `;
2931
-
2932
- /**
2933
- * @license
2934
- * Copyright 2023 Google LLC
2935
- * SPDX-License-Identifier: Apache-2.0
2936
- */
2937
- /**
2938
- * @summary
2939
- * Select menus display a list of choices on temporary surfaces and display the
2940
- * currently selected menu item above the menu.
2941
- *
2942
- * @description
2943
- * The select component allows users to choose a value from a fixed list of
2944
- * available options. Composed of an interactive anchor button and a menu, it is
2945
- * analogous to the native HTML `<select>` element. This is the "outlined"
2946
- * variant.
2947
- *
2948
- * @example
2949
- * ```html
2950
- * <md-outlined-select label="fruits">
2951
- * <!-- An empty selected option will give select an "un-filled" state -->
2952
- * <md-select-option selected></md-select-option>
2953
- * <md-select-option value="apple" headline="Apple"></md-select-option>
2954
- * <md-select-option value="banana" headline="Banana"></md-select-option>
2955
- * <md-select-option value="kiwi" headline="Kiwi"></md-select-option>
2956
- * <md-select-option value="orange" headline="Orange"></md-select-option>
2957
- * <md-select-option value="tomato" headline="Tomato"></md-select-option>
2958
- * </md-outlined-select>
2959
- * ```
2960
- *
2961
- * @final
2962
- * @suppress {visibility}
2963
- */
2964
- let MdOutlinedSelect = class MdOutlinedSelect extends OutlinedSelect {};
2965
- MdOutlinedSelect.styles = [styles$1, styles$2];
2966
- MdOutlinedSelect = __decorate([t('md-outlined-select')], MdOutlinedSelect);
2967
-
2968
- /**
2969
- * @license
2970
- * Copyright 2024 Google LLC
2971
- * SPDX-License-Identifier: Apache-2.0
2972
- */
2973
- // Generated stylesheet for ./menu/internal/menuitem/menu-item-styles.css.
2974
- const styles = i$1`:host{display:flex;--md-ripple-hover-color: var(--md-menu-item-hover-state-layer-color, var(--md-sys-color-on-surface, #1d1b20));--md-ripple-hover-opacity: var(--md-menu-item-hover-state-layer-opacity, 0.08);--md-ripple-pressed-color: var(--md-menu-item-pressed-state-layer-color, var(--md-sys-color-on-surface, #1d1b20));--md-ripple-pressed-opacity: var(--md-menu-item-pressed-state-layer-opacity, 0.12)}:host([disabled]){opacity:var(--md-menu-item-disabled-opacity, 0.3);pointer-events:none}md-focus-ring{z-index:1;--md-focus-ring-shape: 8px}a,button,li{background:none;border:none;padding:0;margin:0;text-align:unset;text-decoration:none}.list-item{border-radius:inherit;display:flex;flex:1;max-width:inherit;min-width:inherit;outline:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.list-item:not(.disabled){cursor:pointer}[slot=container]{pointer-events:none}md-ripple{border-radius:inherit}md-item{border-radius:inherit;flex:1;color:var(--md-menu-item-label-text-color, var(--md-sys-color-on-surface, #1d1b20));font-family:var(--md-menu-item-label-text-font, var(--md-sys-typescale-body-large-font, var(--md-ref-typeface-plain, Roboto)));font-size:var(--md-menu-item-label-text-size, var(--md-sys-typescale-body-large-size, 1rem));line-height:var(--md-menu-item-label-text-line-height, var(--md-sys-typescale-body-large-line-height, 1.5rem));font-weight:var(--md-menu-item-label-text-weight, var(--md-sys-typescale-body-large-weight, var(--md-ref-typeface-weight-regular, 400)));min-height:var(--md-menu-item-one-line-container-height, 56px);padding-top:var(--md-menu-item-top-space, 12px);padding-bottom:var(--md-menu-item-bottom-space, 12px);padding-inline-start:var(--md-menu-item-leading-space, 16px);padding-inline-end:var(--md-menu-item-trailing-space, 16px)}md-item[multiline]{min-height:var(--md-menu-item-two-line-container-height, 72px)}[slot=supporting-text]{color:var(--md-menu-item-supporting-text-color, var(--md-sys-color-on-surface-variant, #49454f));font-family:var(--md-menu-item-supporting-text-font, var(--md-sys-typescale-body-medium-font, var(--md-ref-typeface-plain, Roboto)));font-size:var(--md-menu-item-supporting-text-size, var(--md-sys-typescale-body-medium-size, 0.875rem));line-height:var(--md-menu-item-supporting-text-line-height, var(--md-sys-typescale-body-medium-line-height, 1.25rem));font-weight:var(--md-menu-item-supporting-text-weight, var(--md-sys-typescale-body-medium-weight, var(--md-ref-typeface-weight-regular, 400)))}[slot=trailing-supporting-text]{color:var(--md-menu-item-trailing-supporting-text-color, var(--md-sys-color-on-surface-variant, #49454f));font-family:var(--md-menu-item-trailing-supporting-text-font, var(--md-sys-typescale-label-small-font, var(--md-ref-typeface-plain, Roboto)));font-size:var(--md-menu-item-trailing-supporting-text-size, var(--md-sys-typescale-label-small-size, 0.6875rem));line-height:var(--md-menu-item-trailing-supporting-text-line-height, var(--md-sys-typescale-label-small-line-height, 1rem));font-weight:var(--md-menu-item-trailing-supporting-text-weight, var(--md-sys-typescale-label-small-weight, var(--md-ref-typeface-weight-medium, 500)))}:is([slot=start],[slot=end])::slotted(*){fill:currentColor}[slot=start]{color:var(--md-menu-item-leading-icon-color, var(--md-sys-color-on-surface-variant, #49454f))}[slot=end]{color:var(--md-menu-item-trailing-icon-color, var(--md-sys-color-on-surface-variant, #49454f))}.list-item{background-color:var(--md-menu-item-container-color, transparent)}.list-item.selected{background-color:var(--md-menu-item-selected-container-color, var(--md-sys-color-secondary-container, #e8def8))}.selected:not(.disabled) ::slotted(*){color:var(--md-menu-item-selected-label-text-color, var(--md-sys-color-on-secondary-container, #1d192b))}@media(forced-colors: active){:host([disabled]),:host([disabled]) slot{color:GrayText;opacity:1}.list-item{position:relative}.list-item.selected::before{content:"";position:absolute;inset:0;box-sizing:border-box;border-radius:inherit;pointer-events:none;border:3px double CanvasText}}
2975
- `;
2976
-
2977
- /**
2978
- * @license
2979
- * Copyright 2023 Google LLC
2980
- * SPDX-License-Identifier: Apache-2.0
2981
- */
2982
- /**
2983
- * A controller that provides most functionality of an element that implements
2984
- * the MenuItem interface.
2985
- */
2986
- class MenuItemController {
2987
- /**
2988
- * @param host The MenuItem in which to attach this controller to.
2989
- * @param config The object that configures this controller's behavior.
2990
- */
2991
- constructor(host, config) {
2992
- this.host = host;
2993
- this.internalTypeaheadText = null;
2994
- /**
2995
- * Bind this click listener to the interactive element. Handles closing the
2996
- * menu.
2997
- */
2998
- this.onClick = () => {
2999
- if (this.host.keepOpen) return;
3000
- this.host.dispatchEvent(createDefaultCloseMenuEvent(this.host, {
3001
- kind: CloseReason.CLICK_SELECTION
3002
- }));
3003
- };
3004
- /**
3005
- * Bind this click listener to the interactive element. Handles closing the
3006
- * menu.
3007
- */
3008
- this.onKeydown = event => {
3009
- // Check if the interactive element is an anchor tag. If so, click it.
3010
- if (this.host.href && event.code === 'Enter') {
3011
- const interactiveElement = this.getInteractiveElement();
3012
- if (interactiveElement instanceof HTMLAnchorElement) {
3013
- interactiveElement.click();
3014
- }
3015
- }
3016
- if (event.defaultPrevented) return;
3017
- // If the host has keepOpen = true we should ignore clicks & Space/Enter,
3018
- // however we always maintain the ability to close a menu with a explicit
3019
- // `escape` keypress.
3020
- const keyCode = event.code;
3021
- if (this.host.keepOpen && keyCode !== 'Escape') return;
3022
- if (isClosableKey(keyCode)) {
3023
- event.preventDefault();
3024
- this.host.dispatchEvent(createDefaultCloseMenuEvent(this.host, {
3025
- kind: CloseReason.KEYDOWN,
3026
- key: keyCode
3027
- }));
3028
- }
3029
- };
3030
- this.getHeadlineElements = config.getHeadlineElements;
3031
- this.getSupportingTextElements = config.getSupportingTextElements;
3032
- this.getDefaultElements = config.getDefaultElements;
3033
- this.getInteractiveElement = config.getInteractiveElement;
3034
- this.host.addController(this);
3035
- }
3036
- /**
3037
- * The text that is selectable via typeahead. If not set, defaults to the
3038
- * innerText of the item slotted into the `"headline"` slot, and if there are
3039
- * no slotted elements into headline, then it checks the _default_ slot, and
3040
- * then the `"supporting-text"` slot if nothing is in _default_.
3041
- */
3042
- get typeaheadText() {
3043
- if (this.internalTypeaheadText !== null) {
3044
- return this.internalTypeaheadText;
3045
- }
3046
- const headlineElements = this.getHeadlineElements();
3047
- const textParts = [];
3048
- headlineElements.forEach(headlineElement => {
3049
- if (headlineElement.textContent && headlineElement.textContent.trim()) {
3050
- textParts.push(headlineElement.textContent.trim());
3051
- }
3052
- });
3053
- // If there are no headline elements, check the default slot's text content
3054
- if (textParts.length === 0) {
3055
- this.getDefaultElements().forEach(defaultElement => {
3056
- if (defaultElement.textContent && defaultElement.textContent.trim()) {
3057
- textParts.push(defaultElement.textContent.trim());
3058
- }
3059
- });
3060
- }
3061
- // If there are no headline nor default slot elements, check the
3062
- //supporting-text slot's text content
3063
- if (textParts.length === 0) {
3064
- this.getSupportingTextElements().forEach(supportingTextElement => {
3065
- if (supportingTextElement.textContent && supportingTextElement.textContent.trim()) {
3066
- textParts.push(supportingTextElement.textContent.trim());
3067
- }
3068
- });
3069
- }
3070
- return textParts.join(' ');
3071
- }
3072
- /**
3073
- * The recommended tag name to render as the list item.
3074
- */
3075
- get tagName() {
3076
- const type = this.host.type;
3077
- switch (type) {
3078
- case 'link':
3079
- return 'a';
3080
- case 'button':
3081
- return 'button';
3082
- default:
3083
- case 'menuitem':
3084
- case 'option':
3085
- return 'li';
3086
- }
3087
- }
3088
- /**
3089
- * The recommended role of the menu item.
3090
- */
3091
- get role() {
3092
- return this.host.type === 'option' ? 'option' : 'menuitem';
3093
- }
3094
- hostConnected() {
3095
- this.host.toggleAttribute('md-menu-item', true);
3096
- }
3097
- hostUpdate() {
3098
- if (this.host.href) {
3099
- this.host.type = 'link';
3100
- }
3101
- }
3102
- /**
3103
- * Use to set the typeaheadText when it changes.
3104
- */
3105
- setTypeaheadText(text) {
3106
- this.internalTypeaheadText = text;
3107
- }
3108
- }
3109
-
3110
- /**
3111
- * @license
3112
- * Copyright 2023 Google LLC
3113
- * SPDX-License-Identifier: Apache-2.0
3114
- */
3115
- /**
3116
- * Creates an event fired by a SelectOption to request selection from md-select.
3117
- * Typically fired after `selected` changes from `false` to `true`.
3118
- */
3119
- function createRequestSelectionEvent() {
3120
- return new Event('request-selection', {
3121
- bubbles: true,
3122
- composed: true
3123
- });
3124
- }
3125
- /**
3126
- * Creates an event fired by a SelectOption to request deselection from
3127
- * md-select. Typically fired after `selected` changes from `true` to `false`.
3128
- */
3129
- function createRequestDeselectionEvent() {
3130
- return new Event('request-deselection', {
3131
- bubbles: true,
3132
- composed: true
3133
- });
3134
- }
3135
- /**
3136
- * A controller that provides most functionality and md-select compatibility for
3137
- * an element that implements the SelectOption interface.
3138
- */
3139
- class SelectOptionController {
3140
- /**
3141
- * The recommended role of the select option.
3142
- */
3143
- get role() {
3144
- return this.menuItemController.role;
3145
- }
3146
- /**
3147
- * The text that is selectable via typeahead. If not set, defaults to the
3148
- * innerText of the item slotted into the `"headline"` slot, and if there are
3149
- * no slotted elements into headline, then it checks the _default_ slot, and
3150
- * then the `"supporting-text"` slot if nothing is in _default_.
3151
- */
3152
- get typeaheadText() {
3153
- return this.menuItemController.typeaheadText;
3154
- }
3155
- setTypeaheadText(text) {
3156
- this.menuItemController.setTypeaheadText(text);
3157
- }
3158
- /**
3159
- * The text that is displayed in the select field when selected. If not set,
3160
- * defaults to the textContent of the item slotted into the `"headline"` slot,
3161
- * and if there are no slotted elements into headline, then it checks the
3162
- * _default_ slot, and then the `"supporting-text"` slot if nothing is in
3163
- * _default_.
3164
- */
3165
- get displayText() {
3166
- if (this.internalDisplayText !== null) {
3167
- return this.internalDisplayText;
3168
- }
3169
- return this.menuItemController.typeaheadText;
3170
- }
3171
- setDisplayText(text) {
3172
- this.internalDisplayText = text;
3173
- }
3174
- /**
3175
- * @param host The SelectOption in which to attach this controller to.
3176
- * @param config The object that configures this controller's behavior.
3177
- */
3178
- constructor(host, config) {
3179
- this.host = host;
3180
- this.internalDisplayText = null;
3181
- this.firstUpdate = true;
3182
- /**
3183
- * Bind this click listener to the interactive element. Handles closing the
3184
- * menu.
3185
- */
3186
- this.onClick = () => {
3187
- this.menuItemController.onClick();
3188
- };
3189
- /**
3190
- * Bind this click listener to the interactive element. Handles closing the
3191
- * menu.
3192
- */
3193
- this.onKeydown = e => {
3194
- this.menuItemController.onKeydown(e);
3195
- };
3196
- this.lastSelected = this.host.selected;
3197
- this.menuItemController = new MenuItemController(host, config);
3198
- host.addController(this);
3199
- }
3200
- hostUpdate() {
3201
- if (this.lastSelected !== this.host.selected) {
3202
- this.host.ariaSelected = this.host.selected ? 'true' : 'false';
3203
- }
3204
- }
3205
- hostUpdated() {
3206
- // Do not dispatch event on first update / boot-up.
3207
- if (this.lastSelected !== this.host.selected && !this.firstUpdate) {
3208
- // This section is really useful for when the user sets selected on the
3209
- // option programmatically. Most other cases (click and keyboard) are
3210
- // handled by md-select because it needs to coordinate the
3211
- // single-selection behavior.
3212
- if (this.host.selected) {
3213
- this.host.dispatchEvent(createRequestSelectionEvent());
3214
- } else {
3215
- this.host.dispatchEvent(createRequestDeselectionEvent());
3216
- }
3217
- }
3218
- this.lastSelected = this.host.selected;
3219
- this.firstUpdate = false;
3220
- }
3221
- }
3222
-
3223
- /**
3224
- * @license
3225
- * Copyright 2023 Google LLC
3226
- * SPDX-License-Identifier: Apache-2.0
3227
- */
3228
- // Separate variable needed for closure.
3229
- const selectOptionBaseClass = mixinDelegatesAria(i);
3230
- /**
3231
- * @fires close-menu {CustomEvent<{initiator: SelectOption, reason: Reason, itemPath: SelectOption[]}>}
3232
- * Closes the encapsulating menu on closable interaction. --bubbles --composed
3233
- * @fires request-selection {Event} Requests the parent md-select to select this
3234
- * element (and deselect others if single-selection) when `selected` changed to
3235
- * `true`. --bubbles --composed
3236
- * @fires request-deselection {Event} Requests the parent md-select to deselect
3237
- * this element when `selected` changed to `false`. --bubbles --composed
3238
- */
3239
- class SelectOptionEl extends selectOptionBaseClass {
3240
- constructor() {
3241
- super(...arguments);
3242
- /**
3243
- * Disables the item and makes it non-selectable and non-interactive.
3244
- */
3245
- this.disabled = false;
3246
- /**
3247
- * READONLY: self-identifies as a menu item and sets its identifying attribute
3248
- */
3249
- this.isMenuItem = true;
3250
- /**
3251
- * Sets the item in the selected visual state when a submenu is opened.
3252
- */
3253
- this.selected = false;
3254
- /**
3255
- * Form value of the option.
3256
- */
3257
- this.value = '';
3258
- this.type = 'option';
3259
- this.selectOptionController = new SelectOptionController(this, {
3260
- getHeadlineElements: () => {
3261
- return this.headlineElements;
3262
- },
3263
- getSupportingTextElements: () => {
3264
- return this.supportingTextElements;
3265
- },
3266
- getDefaultElements: () => {
3267
- return this.defaultElements;
3268
- },
3269
- getInteractiveElement: () => this.listItemRoot
3270
- });
3271
- }
3272
- /**
3273
- * The text that is selectable via typeahead. If not set, defaults to the
3274
- * innerText of the item slotted into the `"headline"` slot.
3275
- */
3276
- get typeaheadText() {
3277
- return this.selectOptionController.typeaheadText;
3278
- }
3279
- set typeaheadText(text) {
3280
- this.selectOptionController.setTypeaheadText(text);
3281
- }
3282
- /**
3283
- * The text that is displayed in the select field when selected. If not set,
3284
- * defaults to the textContent of the item slotted into the `"headline"` slot.
3285
- */
3286
- get displayText() {
3287
- return this.selectOptionController.displayText;
3288
- }
3289
- set displayText(text) {
3290
- this.selectOptionController.setDisplayText(text);
3291
- }
3292
- render() {
3293
- return this.renderListItem(b`
3294
- <md-item>
3295
- <div slot="container">
3296
- ${this.renderRipple()} ${this.renderFocusRing()}
3297
- </div>
3298
- <slot name="start" slot="start"></slot>
3299
- <slot name="end" slot="end"></slot>
3300
- ${this.renderBody()}
3301
- </md-item>
3302
- `);
3303
- }
3304
- /**
3305
- * Renders the root list item.
3306
- *
3307
- * @param content the child content of the list item.
3308
- */
3309
- renderListItem(content) {
3310
- return b`
3311
- <li
3312
- id="item"
3313
- tabindex=${this.disabled ? -1 : 0}
3314
- role=${this.selectOptionController.role}
3315
- aria-label=${this.ariaLabel || A}
3316
- aria-selected=${this.ariaSelected || A}
3317
- aria-checked=${this.ariaChecked || A}
3318
- aria-expanded=${this.ariaExpanded || A}
3319
- aria-haspopup=${this.ariaHasPopup || A}
3320
- class="list-item ${e$2(this.getRenderClasses())}"
3321
- @click=${this.selectOptionController.onClick}
3322
- @keydown=${this.selectOptionController.onKeydown}
3323
- >${content}</li
3324
- >
3325
- `;
3326
- }
3327
- /**
3328
- * Handles rendering of the ripple element.
3329
- */
3330
- renderRipple() {
3331
- return b` <md-ripple
3332
- part="ripple"
3333
- for="item"
3334
- ?disabled=${this.disabled}></md-ripple>`;
3335
- }
3336
- /**
3337
- * Handles rendering of the focus ring.
3338
- */
3339
- renderFocusRing() {
3340
- return b` <md-focus-ring
3341
- part="focus-ring"
3342
- for="item"
3343
- inward></md-focus-ring>`;
3344
- }
3345
- /**
3346
- * Classes applied to the list item root.
3347
- */
3348
- getRenderClasses() {
3349
- return {
3350
- 'disabled': this.disabled,
3351
- 'selected': this.selected
3352
- };
3353
- }
3354
- /**
3355
- * Handles rendering the headline and supporting text.
3356
- */
3357
- renderBody() {
3358
- return b`
3359
- <slot></slot>
3360
- <slot name="overline" slot="overline"></slot>
3361
- <slot name="headline" slot="headline"></slot>
3362
- <slot name="supporting-text" slot="supporting-text"></slot>
3363
- <slot
3364
- name="trailing-supporting-text"
3365
- slot="trailing-supporting-text"></slot>
3366
- `;
3367
- }
3368
- focus() {
3369
- var _this$listItemRoot;
3370
- // TODO(b/300334509): needed for some cases where delegatesFocus doesn't
3371
- // work programmatically like in FF and select-option
3372
- (_this$listItemRoot = this.listItemRoot) === null || _this$listItemRoot === void 0 || _this$listItemRoot.focus();
3373
- }
3374
- }
3375
- /** @nocollapse */
3376
- SelectOptionEl.shadowRootOptions = {
3377
- ...i.shadowRootOptions,
3378
- delegatesFocus: true
3379
- };
3380
- __decorate([n$1({
3381
- type: Boolean,
3382
- reflect: true
3383
- })], SelectOptionEl.prototype, "disabled", void 0);
3384
- __decorate([n$1({
3385
- type: Boolean,
3386
- attribute: 'md-menu-item',
3387
- reflect: true
3388
- })], SelectOptionEl.prototype, "isMenuItem", void 0);
3389
- __decorate([n$1({
3390
- type: Boolean
3391
- })], SelectOptionEl.prototype, "selected", void 0);
3392
- __decorate([n$1()], SelectOptionEl.prototype, "value", void 0);
3393
- __decorate([e$1('.list-item')], SelectOptionEl.prototype, "listItemRoot", void 0);
3394
- __decorate([o({
3395
- slot: 'headline'
3396
- })], SelectOptionEl.prototype, "headlineElements", void 0);
3397
- __decorate([o({
3398
- slot: 'supporting-text'
3399
- })], SelectOptionEl.prototype, "supportingTextElements", void 0);
3400
- __decorate([n({
3401
- slot: ''
3402
- })], SelectOptionEl.prototype, "defaultElements", void 0);
3403
- __decorate([n$1({
3404
- attribute: 'typeahead-text'
3405
- })], SelectOptionEl.prototype, "typeaheadText", null);
3406
- __decorate([n$1({
3407
- attribute: 'display-text'
3408
- })], SelectOptionEl.prototype, "displayText", null);
3409
-
3410
- /**
3411
- * @license
3412
- * Copyright 2023 Google LLC
3413
- * SPDX-License-Identifier: Apache-2.0
3414
- */
3415
- /**
3416
- * @summary
3417
- * Select menus display a list of choices on temporary surfaces and display the
3418
- * currently selected menu item above the menu.
3419
- *
3420
- * @description
3421
- * The select component allows users to choose a value from a fixed list of
3422
- * available options. Composed of an interactive anchor button and a menu, it is
3423
- * analogous to the native HTML `<select>` element. This is the option that
3424
- * can be placed inside of an md-select.
3425
- *
3426
- * This component is a subclass of `md-menu-item` and can accept the same slots,
3427
- * properties, and events as `md-menu-item`.
3428
- *
3429
- * @example
3430
- * ```html
3431
- * <md-outlined-select label="fruits">
3432
- * <!-- An empty selected option will give select an "un-filled" state -->
3433
- * <md-select-option selected></md-select-option>
3434
- * <md-select-option value="apple" headline="Apple"></md-select-option>
3435
- * <md-select-option value="banana" headline="Banana"></md-select-option>
3436
- * <md-select-option value="kiwi" headline="Kiwi"></md-select-option>
3437
- * <md-select-option value="orange" headline="Orange"></md-select-option>
3438
- * <md-select-option value="tomato" headline="Tomato"></md-select-option>
3439
- * </md-outlined-select>
3440
- * ```
3441
- *
3442
- * @final
3443
- * @suppress {visibility}
3444
- */
3445
- let MdSelectOption = class MdSelectOption extends SelectOptionEl {};
3446
- MdSelectOption.styles = [styles];
3447
- MdSelectOption = __decorate([t('md-select-option')], MdSelectOption);
3448
-
3449
- var __defProp$1 = Object.defineProperty;
3450
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
3451
- var __decorateClass$1 = (decorators, target, key, kind) => {
3452
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
3453
- for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
3454
- if (kind && result) __defProp$1(target, key, result);
3455
- return result;
3456
- };
3457
- const LOG_LEVELS = [{
3458
- value: "critical",
3459
- label: "Critical"
3460
- }, {
3461
- value: "error",
3462
- label: "Error"
3463
- }, {
3464
- value: "warning",
3465
- label: "Warning"
3466
- }, {
3467
- value: "info",
3468
- label: "Info"
3469
- }, {
3470
- value: "debug",
3471
- label: "Debug"
3472
- }];
3473
- let LogLevelSection = class extends i {
3474
- constructor() {
3475
- super(...arguments);
3476
- this._consoleLevel = "info";
3477
- this._fileLevel = null;
3478
- this._loading = true;
3479
- this._applying = false;
3480
- }
3481
- connectedCallback() {
3482
- super.connectedCallback();
3483
- fireAndForget(this._loadLogLevels());
3484
- }
3485
- async _loadLogLevels() {
3486
- try {
3487
- const result = await this.client.getLogLevel();
3488
- this._consoleLevel = result.console_loglevel;
3489
- this._fileLevel = result.file_loglevel;
3490
- } catch (err) {
3491
- console.error("Failed to load log levels:", err);
3492
- } finally {
3493
- this._loading = false;
3494
- }
3495
- }
3496
- async _apply() {
3497
- this._applying = true;
3498
- try {
3499
- var _this$_fileSelect;
3500
- const consoleLevel = this._consoleSelect.value;
3501
- const fileLevel = (_this$_fileSelect = this._fileSelect) === null || _this$_fileSelect === void 0 ? void 0 : _this$_fileSelect.value;
3502
- const result = await this.client.setLogLevel(consoleLevel, this._fileLevel !== null ? fileLevel : void 0);
3503
- this._consoleLevel = result.console_loglevel;
3504
- this._fileLevel = result.file_loglevel;
3505
- this.dispatchEvent(new CustomEvent("log-level-applied", {
3506
- bubbles: true,
3507
- composed: true
3508
- }));
3509
- } catch (err) {
3510
- console.error("Failed to apply log levels:", err);
3511
- showAlertDialog({
3512
- title: "Error",
3513
- text: "Failed to apply log levels"
3514
- });
3515
- } finally {
3516
- this._applying = false;
3517
- }
3518
- }
3519
- render() {
3520
- if (this._loading) {
3521
- return b` <p class="loading">Loading...</p> `;
3522
- }
3523
- return b`
3524
- <p class="hint">Changes are temporary and will be reset on the next server restart.</p>
3525
- <div class="form-field">
3526
- <label>Console Log Level</label>
3527
- <md-outlined-select name="console" .value=${this._consoleLevel}>
3528
- ${LOG_LEVELS.map(level => b`
3529
- <md-select-option value=${level.value} ?selected=${level.value === this._consoleLevel}>
3530
- <div slot="headline">${level.label}</div>
3531
- </md-select-option>
3532
- `)}
3533
- </md-outlined-select>
3534
- </div>
3535
- ${this._fileLevel !== null ? b`
3536
- <div class="form-field">
3537
- <label>File Log Level</label>
3538
- <md-outlined-select name="file" .value=${this._fileLevel}>
3539
- ${LOG_LEVELS.map(level => b`
3540
- <md-select-option
3541
- value=${level.value}
3542
- ?selected=${level.value === this._fileLevel}
3543
- >
3544
- <div slot="headline">${level.label}</div>
3545
- </md-select-option>
3546
- `)}
3547
- </md-outlined-select>
3548
- </div>
3549
- ` : A}
3550
- <div class="actions">
3551
- <md-text-button @click=${handleAsync(() => this._apply())} ?disabled=${this._applying}>
3552
- ${this._applying ? "Applying..." : "Apply"}
3553
- </md-text-button>
3554
- </div>
3555
- `;
3556
- }
3557
- };
3558
- LogLevelSection.styles = i$1`
3559
- :host {
3560
- display: block;
3561
- }
3562
-
3563
- .loading {
3564
- text-align: center;
3565
- padding: 24px;
3566
- color: var(--md-sys-color-on-surface-variant);
3567
- }
3568
-
3569
- .hint {
3570
- font-size: 0.875rem;
3571
- color: var(--md-sys-color-on-surface-variant);
3572
- margin: 0 0 16px 0;
3573
- font-style: italic;
3574
- }
3575
-
3576
- .form-field {
3577
- margin-bottom: 16px;
3578
- }
3579
-
3580
- .form-field label {
3581
- display: block;
3582
- margin-bottom: 8px;
3583
- font-weight: 500;
3584
- color: var(--md-sys-color-on-surface);
3585
- }
3586
-
3587
- md-outlined-select {
3588
- width: 100%;
3589
- }
3590
-
3591
- .actions {
3592
- display: flex;
3593
- justify-content: flex-end;
3594
- margin-top: 8px;
3595
- }
3596
- `;
3597
- __decorateClass$1([n$1({
3598
- attribute: false
3599
- })], LogLevelSection.prototype, "client", 2);
3600
- __decorateClass$1([r()], LogLevelSection.prototype, "_consoleLevel", 2);
3601
- __decorateClass$1([r()], LogLevelSection.prototype, "_fileLevel", 2);
3602
- __decorateClass$1([r()], LogLevelSection.prototype, "_loading", 2);
3603
- __decorateClass$1([r()], LogLevelSection.prototype, "_applying", 2);
3604
- __decorateClass$1([e$1("md-outlined-select[name='console']")], LogLevelSection.prototype, "_consoleSelect", 2);
3605
- __decorateClass$1([e$1("md-outlined-select[name='file']")], LogLevelSection.prototype, "_fileSelect", 2);
3606
- LogLevelSection = __decorateClass$1([t("log-level-section")], LogLevelSection);
3607
-
3608
- var __defProp = Object.defineProperty;
3609
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3610
- var __decorateClass = (decorators, target, key, kind) => {
3611
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
3612
- for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
3613
- if (kind && result) __defProp(target, key, result);
3614
- return result;
3615
- };
3616
- let SettingsDialog = class extends i {
3617
- constructor() {
3618
- super(...arguments);
3619
- this._devMode = DevModeService.active;
3620
- this._expandedRow = null;
3621
- this._credLoading = false;
3622
- this._showPassword = false;
3623
- }
3624
- connectedCallback() {
3625
- super.connectedCallback();
3626
- this._unsubscribeDev = DevModeService.subscribe(active => {
3627
- this._devMode = active;
3628
- });
3629
- }
3630
- disconnectedCallback() {
3631
- var _this$_unsubscribeDev;
3632
- super.disconnectedCallback();
3633
- (_this$_unsubscribeDev = this._unsubscribeDev) === null || _this$_unsubscribeDev === void 0 || _this$_unsubscribeDev.call(this);
3634
- }
3635
- firstUpdated() {
3636
- const knownSections = /* @__PURE__ */new Set(["network-credentials"]);
3637
- if (this.scrollToSection && knownSections.has(this.scrollToSection)) {
3638
- requestAnimationFrame(() => {
3639
- var _this$renderRoot$quer;
3640
- (_this$renderRoot$quer = this.renderRoot.querySelector(`#${this.scrollToSection}`)) === null || _this$renderRoot$quer === void 0 || _this$renderRoot$quer.scrollIntoView({
3641
- behavior: "smooth",
3642
- block: "start"
3643
- });
3644
- });
3645
- }
3646
- }
3647
- _close() {
3648
- this.shadowRoot.querySelector("md-dialog").close();
3649
- }
3650
- _handleClosed() {
3651
- this.parentNode.removeChild(this);
3652
- }
3653
- _onDevToggle(event) {
3654
- const target = event.target;
3655
- DevModeService.setActive(target.selected);
3656
- }
3657
- _copyDevLink() {
3658
- var _navigator$clipboard;
3659
- const url = new URL(window.location.href);
3660
- url.searchParams.set("dev", "on");
3661
- (_navigator$clipboard = navigator.clipboard) === null || _navigator$clipboard === void 0 || _navigator$clipboard.writeText(url.toString()).catch(() => {});
3662
- }
3663
- _toggleExpand(row) {
3664
- this._expandedRow = this._expandedRow === row ? null : row;
3665
- this._showPassword = false;
3666
- }
3667
- _cancelCred() {
3668
- this._expandedRow = null;
3669
- this._showPassword = false;
3670
- }
3671
- _togglePassword() {
3672
- this._showPassword = !this._showPassword;
3673
- }
3674
- async _saveWifi() {
3675
- const ssid = this._wifiSsidField.value.trim();
3676
- if (!ssid) {
3677
- showAlertDialog({
3678
- title: "Validation error",
3679
- text: "SSID is required"
3680
- });
3681
- return;
3682
- }
3683
- const password = this._wifiPasswordField.value;
3684
- if (!password) {
3685
- showAlertDialog({
3686
- title: "Validation error",
3687
- text: "Password is required"
3688
- });
3689
- return;
3690
- }
3691
- this._credLoading = true;
3692
- try {
3693
- await this.client.setWifiCredentials(ssid, password);
3694
- this._expandedRow = null;
3695
- this._showPassword = false;
3696
- } catch (err) {
3697
- showAlertDialog({
3698
- title: "Error saving WiFi credentials",
3699
- text: err.message
3700
- });
3701
- } finally {
3702
- this._credLoading = false;
3703
- }
3704
- }
3705
- async _removeWifi() {
3706
- this._credLoading = true;
3707
- try {
3708
- await this.client.removeWifiCredentials();
3709
- this._expandedRow = null;
3710
- this._showPassword = false;
3711
- } catch (err) {
3712
- showAlertDialog({
3713
- title: "Error removing WiFi credentials",
3714
- text: err.message
3715
- });
3716
- } finally {
3717
- this._credLoading = false;
3718
- }
3719
- }
3720
- async _saveThread() {
3721
- const dataset = this._threadDatasetField.value.trim();
3722
- if (!dataset) {
3723
- showAlertDialog({
3724
- title: "Validation error",
3725
- text: "Thread dataset is required"
3726
- });
3727
- return;
3728
- }
3729
- if (!/^[0-9a-fA-F]*$/.test(dataset) || dataset.length % 2 !== 0) {
3730
- showAlertDialog({
3731
- title: "Invalid Thread dataset",
3732
- text: "Must be a hex string with even length (each byte is two hex characters)"
3733
- });
3734
- return;
3735
- }
3736
- this._credLoading = true;
3737
- try {
3738
- await this.client.setThreadOperationalDataset(dataset);
3739
- this._expandedRow = null;
3740
- } catch (err) {
3741
- showAlertDialog({
3742
- title: "Error saving Thread dataset",
3743
- text: err.message
3744
- });
3745
- } finally {
3746
- this._credLoading = false;
3747
- }
3748
- }
3749
- async _removeThread() {
3750
- this._credLoading = true;
3751
- try {
3752
- await this.client.removeThreadDataset();
3753
- this._expandedRow = null;
3754
- this._showPassword = false;
3755
- } catch (err) {
3756
- showAlertDialog({
3757
- title: "Error removing Thread dataset",
3758
- text: err.message
3759
- });
3760
- } finally {
3761
- this._credLoading = false;
3762
- }
3763
- }
3764
- render() {
3765
- return b`
3766
- <md-dialog open @cancel=${preventDefault} @closed=${this._handleClosed}>
3767
- <div slot="headline">Settings</div>
3768
- <div slot="content">
3769
- <section class="section">
3770
- <h3 class="section-title">Developer mode</h3>
3771
- <div class="toggle-row">
3772
- <label for="dev-switch" class="toggle-label">
3773
- Enable developer mode
3774
- <span class="hint">
3775
- Adds raw read/write buttons and a generic command invoker to cluster views.
3776
- Activation is reflected in the URL (<code>?dev=on</code>) and does not persist.
3777
- </span>
3778
- </label>
3779
- <md-switch
3780
- id="dev-switch"
3781
- ?selected=${this._devMode}
3782
- @change=${this._onDevToggle}
3783
- ></md-switch>
3784
- </div>
3785
- <div class="aux-row">
3786
- <md-text-button @click=${this._copyDevLink}>Copy URL with dev enabled</md-text-button>
3787
- </div>
3788
- </section>
3789
-
3790
- <md-divider></md-divider>
3791
-
3792
- <section class="section">
3793
- <h3 class="section-title">Server log levels</h3>
3794
- <log-level-section .client=${this.client}></log-level-section>
3795
- </section>
3796
-
3797
- <md-divider></md-divider>
3798
-
3799
- <section id="network-credentials" class="section">
3800
- <h3 class="section-title">Network credentials</h3>
3801
-
3802
- <div class="cred-row">
3803
- <div class="cred-info">
3804
- <ha-svg-icon .path=${mdiWifi}></ha-svg-icon>
3805
- <span class="cred-label">WiFi</span>
3806
- ${this.client.serverInfo.wifi_credentials_set ? b`<span class="cred-value">${this.client.serverInfo.wifi_ssid}</span>` : b`<span class="cred-unset">Not configured</span>`}
3807
- </div>
3808
- <md-text-button @click=${() => this._toggleExpand("wifi")} .disabled=${this._credLoading}
3809
- >Edit</md-text-button
3810
- >
3811
- </div>
3812
-
3813
- ${this._expandedRow === "wifi" ? b` <div class="cred-form">
3814
- <md-outlined-text-field
3815
- id="cred-wifi-ssid"
3816
- label="SSID"
3817
- .value=${this.client.serverInfo.wifi_ssid ?? ""}
3818
- .disabled=${this._credLoading}
3819
- ></md-outlined-text-field>
3820
- <div class="password-row">
3821
- <md-outlined-text-field
3822
- id="cred-wifi-password"
3823
- label="Password"
3824
- .type=${this._showPassword ? "text" : "password"}
3825
- .disabled=${this._credLoading}
3826
- ></md-outlined-text-field>
3827
- <md-icon-button @click=${this._togglePassword}>
3828
- <ha-svg-icon .path=${this._showPassword ? mdiEyeOff : mdiEye}></ha-svg-icon>
3829
- </md-icon-button>
3830
- </div>
3831
- <div class="form-actions">
3832
- <md-text-button @click=${this._cancelCred} .disabled=${this._credLoading}
3833
- >Cancel</md-text-button
3834
- >
3835
- ${this.client.serverInfo.wifi_credentials_set ? b`<md-text-button
3836
- @click=${handleAsync(() => this._removeWifi())}
3837
- .disabled=${this._credLoading}
3838
- >Remove</md-text-button
3839
- >` : A}
3840
- <md-filled-button
3841
- @click=${handleAsync(() => this._saveWifi())}
3842
- .disabled=${this._credLoading}
3843
- >Save</md-filled-button
3844
- >
3845
- </div>
3846
- </div>` : A}
3847
-
3848
- <div class="cred-row cred-row-thread">
3849
- <div class="cred-info">
3850
- <ha-svg-icon .path=${mdiAccessPoint}></ha-svg-icon>
3851
- <span class="cred-label">Thread</span>
3852
- ${this.client.serverInfo.thread_credentials_set ? b`<span class="cred-value">Thread network set</span>` : b`<span class="cred-unset">Not configured</span>`}
3853
- </div>
3854
- <md-text-button @click=${() => this._toggleExpand("thread")} .disabled=${this._credLoading}
3855
- >Edit</md-text-button
3856
- >
3857
- </div>
3858
-
3859
- ${this._expandedRow === "thread" ? b` <div class="cred-form">
3860
- <md-outlined-text-field
3861
- id="cred-thread-dataset"
3862
- label="Thread dataset"
3863
- supporting-text="Hex string (e.g. 0E080000...)"
3864
- .disabled=${this._credLoading}
3865
- ></md-outlined-text-field>
3866
- <div class="form-actions">
3867
- <md-text-button @click=${this._cancelCred} .disabled=${this._credLoading}
3868
- >Cancel</md-text-button
3869
- >
3870
- ${this.client.serverInfo.thread_credentials_set ? b`<md-text-button
3871
- @click=${handleAsync(() => this._removeThread())}
3872
- .disabled=${this._credLoading}
3873
- >Remove</md-text-button
3874
- >` : A}
3875
- <md-filled-button
3876
- @click=${handleAsync(() => this._saveThread())}
3877
- .disabled=${this._credLoading}
3878
- >Save</md-filled-button
3879
- >
3880
- </div>
3881
- </div>` : A}
3882
-
3883
- <p class="cred-hint">Used when commissioning new devices. Existing devices are not affected.</p>
3884
- </section>
3885
- </div>
3886
- <div slot="actions">
3887
- <md-text-button @click=${this._close}>Close</md-text-button>
3888
- </div>
3889
- </md-dialog>
3890
- `;
3891
- }
3892
- };
3893
- SettingsDialog.styles = i$1`
3894
- md-dialog {
3895
- min-width: 480px;
3896
- max-width: 600px;
3897
- }
3898
-
3899
- .section {
3900
- padding: 8px 0 16px 0;
3901
- }
3902
-
3903
- .section-title {
3904
- margin: 0 0 12px 0;
3905
- font-size: 0.95rem;
3906
- font-weight: 500;
3907
- color: var(--md-sys-color-on-surface);
3908
- text-transform: uppercase;
3909
- letter-spacing: 0.08em;
3910
- }
3911
-
3912
- .toggle-row {
3913
- display: flex;
3914
- align-items: center;
3915
- gap: 16px;
3916
- justify-content: space-between;
3917
- }
3918
-
3919
- .toggle-label {
3920
- display: flex;
3921
- flex-direction: column;
3922
- gap: 4px;
3923
- color: var(--md-sys-color-on-surface);
3924
- font-size: 0.95rem;
3925
- }
3926
-
3927
- .hint {
3928
- font-size: 0.825rem;
3929
- color: var(--md-sys-color-on-surface-variant);
3930
- font-weight: 400;
3931
- }
3932
-
3933
- .hint code {
3934
- font-family: var(--monospace-font);
3935
- background: var(--md-sys-color-surface-container-high);
3936
- padding: 0 4px;
3937
- border-radius: 3px;
3938
- }
3939
-
3940
- .aux-row {
3941
- margin-top: 8px;
3942
- display: flex;
3943
- justify-content: flex-end;
3944
- }
3945
-
3946
- md-divider {
3947
- margin: 12px 0;
3948
- }
3949
-
3950
- .cred-row {
3951
- display: flex;
3952
- align-items: center;
3953
- justify-content: space-between;
3954
- padding: 4px 0;
3955
- }
3956
-
3957
- .cred-row-thread {
3958
- margin-top: 8px;
3959
- }
3960
-
3961
- .cred-info {
3962
- display: flex;
3963
- align-items: center;
3964
- gap: 10px;
3965
- font-size: 0.9rem;
3966
- color: var(--md-sys-color-on-surface);
3967
- }
3968
-
3969
- .cred-label {
3970
- font-weight: 500;
3971
- min-width: 52px;
3972
- }
3973
-
3974
- .cred-value {
3975
- color: var(--md-sys-color-on-surface-variant);
3976
- }
3977
-
3978
- .cred-unset {
3979
- color: var(--md-sys-color-on-surface-variant);
3980
- font-style: italic;
3981
- }
3982
-
3983
- .cred-form {
3984
- display: flex;
3985
- flex-direction: column;
3986
- gap: 10px;
3987
- padding: 8px 0 4px 0;
3988
- }
3989
-
3990
- .password-row {
3991
- display: flex;
3992
- align-items: center;
3993
- gap: 4px;
3994
- }
3995
-
3996
- .password-row md-outlined-text-field {
3997
- flex: 1;
3998
- }
3999
-
4000
- .form-actions {
4001
- display: flex;
4002
- gap: 4px;
4003
- justify-content: flex-end;
4004
- }
4005
-
4006
- .cred-hint {
4007
- margin: 10px 0 0 0;
4008
- font-size: 0.8rem;
4009
- color: var(--md-sys-color-on-surface-variant);
4010
- }
4011
-
4012
- .cred-info ha-svg-icon {
4013
- width: 18px;
4014
- height: 18px;
4015
- color: var(--md-sys-color-on-surface-variant);
4016
- }
4017
-
4018
- .password-row ha-svg-icon {
4019
- width: 18px;
4020
- height: 18px;
4021
- color: var(--md-sys-color-on-surface-variant);
4022
- }
4023
- `;
4024
- __decorateClass([n$1({
4025
- attribute: false
4026
- })], SettingsDialog.prototype, "client", 2);
4027
- __decorateClass([r()], SettingsDialog.prototype, "_devMode", 2);
4028
- __decorateClass([n$1({
4029
- attribute: false
4030
- })], SettingsDialog.prototype, "scrollToSection", 2);
4031
- __decorateClass([r()], SettingsDialog.prototype, "_expandedRow", 2);
4032
- __decorateClass([r()], SettingsDialog.prototype, "_credLoading", 2);
4033
- __decorateClass([r()], SettingsDialog.prototype, "_showPassword", 2);
4034
- __decorateClass([e$1("#cred-wifi-ssid")], SettingsDialog.prototype, "_wifiSsidField", 2);
4035
- __decorateClass([e$1("#cred-wifi-password")], SettingsDialog.prototype, "_wifiPasswordField", 2);
4036
- __decorateClass([e$1("#cred-thread-dataset")], SettingsDialog.prototype, "_threadDatasetField", 2);
4037
- SettingsDialog = __decorateClass([t("settings-dialog")], SettingsDialog);
4038
-
4039
- export { SettingsDialog };