@matter-server/dashboard 0.2.6 → 0.2.7-alpha.0-20260118-45c7af0

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 (53) hide show
  1. package/README.md +16 -0
  2. package/dist/esm/client/models/descriptions.js +1754 -1754
  3. package/dist/esm/components/dialogs/binding/node-binding-dialog.d.ts.map +1 -1
  4. package/dist/esm/components/dialogs/binding/node-binding-dialog.js +8 -4
  5. package/dist/esm/components/dialogs/binding/node-binding-dialog.js.map +1 -1
  6. package/dist/esm/components/dialogs/settings/log-level-dialog.d.ts +33 -0
  7. package/dist/esm/components/dialogs/settings/log-level-dialog.d.ts.map +1 -0
  8. package/dist/esm/components/dialogs/settings/log-level-dialog.js +185 -0
  9. package/dist/esm/components/dialogs/settings/log-level-dialog.js.map +6 -0
  10. package/dist/esm/components/dialogs/settings/show-log-level-dialog.d.ts +8 -0
  11. package/dist/esm/components/dialogs/settings/show-log-level-dialog.d.ts.map +1 -0
  12. package/dist/esm/components/dialogs/settings/show-log-level-dialog.js +15 -0
  13. package/dist/esm/components/dialogs/settings/show-log-level-dialog.js.map +6 -0
  14. package/dist/esm/entrypoint/main.d.ts +1 -1
  15. package/dist/esm/entrypoint/main.d.ts.map +1 -1
  16. package/dist/esm/entrypoint/main.js +1 -0
  17. package/dist/esm/entrypoint/main.js.map +1 -1
  18. package/dist/esm/pages/components/header.d.ts +9 -0
  19. package/dist/esm/pages/components/header.d.ts.map +1 -1
  20. package/dist/esm/pages/components/header.js +68 -6
  21. package/dist/esm/pages/components/header.js.map +1 -1
  22. package/dist/esm/pages/matter-dashboard-app.d.ts +6 -1
  23. package/dist/esm/pages/matter-dashboard-app.d.ts.map +1 -1
  24. package/dist/esm/pages/matter-dashboard-app.js +119 -24
  25. package/dist/esm/pages/matter-dashboard-app.js.map +1 -1
  26. package/dist/esm/util/theme-service.d.ts +27 -0
  27. package/dist/esm/util/theme-service.d.ts.map +1 -0
  28. package/dist/esm/util/theme-service.js +71 -0
  29. package/dist/esm/util/theme-service.js.map +6 -0
  30. package/dist/web/index.html +35 -0
  31. package/dist/web/js/{commission-node-dialog--19-sX9D.js → commission-node-dialog-CoaDIV2Y.js} +5 -5
  32. package/dist/web/js/{commission-node-existing-DY6SnsHb.js → commission-node-existing-DEU_mJjO.js} +5 -4
  33. package/dist/web/js/{commission-node-thread-CXquVvK5.js → commission-node-thread-DZ6DghSs.js} +5 -4
  34. package/dist/web/js/{commission-node-wifi-VQGVOrr7.js → commission-node-wifi-DOyin0q3.js} +5 -4
  35. package/dist/web/js/{dialog-box-qX-alVZJ.js → dialog-box-B5sunUPv.js} +2 -2
  36. package/dist/web/js/{fire_event-B13DcOc9.js → fire_event-C9Duc1j-.js} +1 -1
  37. package/dist/web/js/log-level-dialog-B7LsZYUL.js +3232 -0
  38. package/dist/web/js/main.js +163 -8
  39. package/dist/web/js/{matter-dashboard-app-CU3-L2nl.js → matter-dashboard-app-DlHSE_Qh.js} +13253 -13039
  40. package/dist/web/js/{node-binding-dialog-D4rr_G9I.js → node-binding-dialog-BifZsigR.js} +12 -7
  41. package/dist/web/js/outlined-text-field-D2BOt1yD.js +968 -0
  42. package/dist/web/js/{prevent_default-Dw7ifAL-.js → prevent_default-CuW2EnKR.js} +1 -1
  43. package/dist/web/js/validator-MOJiFndw.js +1122 -0
  44. package/package.json +4 -4
  45. package/src/client/models/descriptions.ts +1754 -1754
  46. package/src/components/dialogs/binding/node-binding-dialog.ts +8 -4
  47. package/src/components/dialogs/settings/log-level-dialog.ts +179 -0
  48. package/src/components/dialogs/settings/show-log-level-dialog.ts +14 -0
  49. package/src/entrypoint/main.ts +1 -0
  50. package/src/pages/components/header.ts +72 -8
  51. package/src/pages/matter-dashboard-app.ts +123 -26
  52. package/src/util/theme-service.ts +98 -0
  53. package/dist/web/js/outlined-text-field-CtlEkpbk.js +0 -2086
@@ -0,0 +1,3232 @@
1
+ import { e, N as NavigableKeys, _ as __decorate, a as e$1, n as n$1, o, r, i, c as createAnimationSignal, L as ListController, g as getActiveItem, b as getLastActivatableItem, d as getFirstActivatableItem, f as e$2, A, h as b, E as EASING, j as i$1, t, D, m as mixinDelegatesAria, k as mixinElementInternals, u, l as i$2 } from './matter-dashboard-app-DlHSE_Qh.js';
2
+ import { r as redispatchEvent, p as preventDefault } from './prevent_default-CuW2EnKR.js';
3
+ import { o as o$1, V as Validator, m as mixinOnReportValidity, a as mixinConstraintValidation, b as mixinFormAssociated, c as onReportValidity, g as getFormValue, d as createValidator, e as getValidityAnchor } from './validator-MOJiFndw.js';
4
+ import './main.js';
5
+
6
+ /**
7
+ * @license
8
+ * Copyright 2017 Google LLC
9
+ * SPDX-License-Identifier: BSD-3-Clause
10
+ */
11
+ function n(n) {
12
+ return (o, r) => {
13
+ const {
14
+ slot: e$1
15
+ } = n ?? {},
16
+ s = "slot" + (e$1 ? `[name=${e$1}]` : ":not([name])");
17
+ return e(o, r, {
18
+ get() {
19
+ var _this$renderRoot;
20
+ const t = (_this$renderRoot = this.renderRoot) === null || _this$renderRoot === void 0 ? void 0 : _this$renderRoot.querySelector(s);
21
+ return (t === null || t === void 0 ? void 0 : t.assignedNodes(n)) ?? [];
22
+ }
23
+ });
24
+ };
25
+ }
26
+
27
+ /**
28
+ * @license
29
+ * Copyright 2023 Google LLC
30
+ * SPDX-License-Identifier: Apache-2.0
31
+ */
32
+ /**
33
+ * Creates an event that closes any parent menus.
34
+ */
35
+ function createCloseMenuEvent(initiator, reason) {
36
+ return new CustomEvent('close-menu', {
37
+ bubbles: true,
38
+ composed: true,
39
+ detail: {
40
+ initiator,
41
+ reason,
42
+ itemPath: [initiator]
43
+ }
44
+ });
45
+ }
46
+ /**
47
+ * Creates a default close menu event used by md-menu.
48
+ */
49
+ const createDefaultCloseMenuEvent = createCloseMenuEvent;
50
+ /**
51
+ * Keys that are used for selection in menus.
52
+ */
53
+ // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
54
+ const SelectionKey = {
55
+ SPACE: 'Space',
56
+ ENTER: 'Enter'
57
+ };
58
+ /**
59
+ * Default close `Reason` kind values.
60
+ */
61
+ // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
62
+ const CloseReason = {
63
+ CLICK_SELECTION: 'click-selection',
64
+ KEYDOWN: 'keydown'
65
+ };
66
+ /**
67
+ * Keys that can close menus.
68
+ */
69
+ // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
70
+ const KeydownCloseKey = {
71
+ ESCAPE: 'Escape',
72
+ SPACE: SelectionKey.SPACE,
73
+ ENTER: SelectionKey.ENTER
74
+ };
75
+ /**
76
+ * Determines whether the given key code is a key code that should close the
77
+ * menu.
78
+ *
79
+ * @param code The KeyboardEvent code to check.
80
+ * @return Whether or not the key code is in the predetermined list to close the
81
+ * menu.
82
+ */
83
+ function isClosableKey(code) {
84
+ return Object.values(KeydownCloseKey).some(value => value === code);
85
+ }
86
+ /**
87
+ * Determines whether the given key code is a key code that should select a menu
88
+ * item.
89
+ *
90
+ * @param code They KeyboardEvent code to check.
91
+ * @return Whether or not the key code is in the predetermined list to select a
92
+ * menu item.
93
+ */
94
+ function isSelectableKey(code) {
95
+ return Object.values(SelectionKey).some(value => value === code);
96
+ }
97
+ /**
98
+ * Determines whether a target element is contained inside another element's
99
+ * composed tree.
100
+ *
101
+ * @param target The potential contained element.
102
+ * @param container The potential containing element of the target.
103
+ * @returns Whether the target element is contained inside the container's
104
+ * composed subtree
105
+ */
106
+ function isElementInSubtree(target, container) {
107
+ // Dispatch a composed, bubbling event to check its path to see if the
108
+ // newly-focused element is contained in container's subtree
109
+ const focusEv = new Event('md-contains', {
110
+ bubbles: true,
111
+ composed: true
112
+ });
113
+ let composedPath = [];
114
+ const listener = ev => {
115
+ composedPath = ev.composedPath();
116
+ };
117
+ container.addEventListener('md-contains', listener);
118
+ target.dispatchEvent(focusEv);
119
+ container.removeEventListener('md-contains', listener);
120
+ const isContained = composedPath.length > 0;
121
+ return isContained;
122
+ }
123
+ /**
124
+ * Element to focus on when menu is first opened.
125
+ */
126
+ // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
127
+ const FocusState = {
128
+ NONE: 'none',
129
+ LIST_ROOT: 'list-root',
130
+ FIRST_ITEM: 'first-item',
131
+ LAST_ITEM: 'last-item'
132
+ };
133
+
134
+ /**
135
+ * @license
136
+ * Copyright 2023 Google LLC
137
+ * SPDX-License-Identifier: Apache-2.0
138
+ */
139
+ /**
140
+ * An enum of supported Menu corners
141
+ */
142
+ // tslint:disable-next-line:enforce-name-casing We are mimicking enum style
143
+ const Corner = {
144
+ END_START: 'end-start',
145
+ START_START: 'start-start'};
146
+ /**
147
+ * Given a surface, an anchor, corners, and some options, this surface will
148
+ * calculate the position of a surface to align the two given corners and keep
149
+ * the surface inside the window viewport. It also provides a StyleInfo map that
150
+ * can be applied to the surface to handle visiblility and position.
151
+ */
152
+ class SurfacePositionController {
153
+ /**
154
+ * @param host The host to connect the controller to.
155
+ * @param getProperties A function that returns the properties for the
156
+ * controller.
157
+ */
158
+ constructor(host, getProperties) {
159
+ this.host = host;
160
+ this.getProperties = getProperties;
161
+ // The current styles to apply to the surface.
162
+ this.surfaceStylesInternal = {
163
+ 'display': 'none'
164
+ };
165
+ // Previous values stored for change detection. Open change detection is
166
+ // calculated separately so initialize it here.
167
+ this.lastValues = {
168
+ isOpen: false
169
+ };
170
+ this.host.addController(this);
171
+ }
172
+ /**
173
+ * The StyleInfo map to apply to the surface via Lit's stylemap
174
+ */
175
+ get surfaceStyles() {
176
+ return this.surfaceStylesInternal;
177
+ }
178
+ /**
179
+ * Calculates the surface's new position required so that the surface's
180
+ * `surfaceCorner` aligns to the anchor's `anchorCorner` while keeping the
181
+ * surface inside the window viewport. This positioning also respects RTL by
182
+ * checking `getComputedStyle()` on the surface element.
183
+ */
184
+ async position() {
185
+ const {
186
+ surfaceEl,
187
+ anchorEl,
188
+ anchorCorner: anchorCornerRaw,
189
+ surfaceCorner: surfaceCornerRaw,
190
+ positioning,
191
+ xOffset,
192
+ yOffset,
193
+ disableBlockFlip,
194
+ disableInlineFlip,
195
+ repositionStrategy
196
+ } = this.getProperties();
197
+ const anchorCorner = anchorCornerRaw.toLowerCase().trim();
198
+ const surfaceCorner = surfaceCornerRaw.toLowerCase().trim();
199
+ if (!surfaceEl || !anchorEl) {
200
+ return;
201
+ }
202
+ // Store these before we potentially resize the window with the next set of
203
+ // lines
204
+ const windowInnerWidth = window.innerWidth;
205
+ const windowInnerHeight = window.innerHeight;
206
+ const div = document.createElement('div');
207
+ div.style.opacity = '0';
208
+ div.style.position = 'fixed';
209
+ div.style.display = 'block';
210
+ div.style.inset = '0';
211
+ document.body.appendChild(div);
212
+ const scrollbarTestRect = div.getBoundingClientRect();
213
+ div.remove();
214
+ // Calculate the widths of the scrollbars in the inline and block directions
215
+ // to account for window-relative calculations.
216
+ const blockScrollbarHeight = window.innerHeight - scrollbarTestRect.bottom;
217
+ const inlineScrollbarWidth = window.innerWidth - scrollbarTestRect.right;
218
+ // Paint the surface transparently so that we can get the position and the
219
+ // rect info of the surface.
220
+ this.surfaceStylesInternal = {
221
+ 'display': 'block',
222
+ 'opacity': '0'
223
+ };
224
+ // Wait for it to be visible.
225
+ this.host.requestUpdate();
226
+ await this.host.updateComplete;
227
+ // Safari has a bug that makes popovers render incorrectly if the node is
228
+ // made visible + Animation Frame before calling showPopover().
229
+ // https://bugs.webkit.org/show_bug.cgi?id=264069
230
+ // also the cast is required due to differing TS types in Google and OSS.
231
+ if (surfaceEl.popover && surfaceEl.isConnected) {
232
+ surfaceEl.showPopover();
233
+ }
234
+ const surfaceRect = surfaceEl.getSurfacePositionClientRect ? surfaceEl.getSurfacePositionClientRect() : surfaceEl.getBoundingClientRect();
235
+ const anchorRect = anchorEl.getSurfacePositionClientRect ? anchorEl.getSurfacePositionClientRect() : anchorEl.getBoundingClientRect();
236
+ const [surfaceBlock, surfaceInline] = surfaceCorner.split('-');
237
+ const [anchorBlock, anchorInline] = anchorCorner.split('-');
238
+ // LTR depends on the direction of the SURFACE not the anchor.
239
+ const isLTR = getComputedStyle(surfaceEl).direction === 'ltr';
240
+ /*
241
+ * For more on inline and block dimensions, see MDN article:
242
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values
243
+ *
244
+ * ┌───── inline/blockDocumentOffset inlineScrollbarWidth
245
+ * │ │ │
246
+ * │ ┌─▼─────┐ │Document
247
+ * │ ┌┼───────┴──────────────────────────────┼────────┐
248
+ * │ ││ │ │
249
+ * └──► ││ ┌───── inline/blockWindowOffset │ │
250
+ * ││ │ │ ▼ │
251
+ * ││ │ ┌─▼───┐ Window┌┐ │
252
+ * └┤ │ ┌┼─────┴───────────────────────┼│ │
253
+ * │ │ ││ ││ │
254
+ * │ └──► ││ ┌──inline/blockAnchorOffset││ │
255
+ * │ ││ │ │ ││ │
256
+ * │ └┤ │ ┌──▼───┐ ││ │
257
+ * │ │ │ ┌┼──────┤ ││ │
258
+ * │ │ └─►│Anchor│ ││ │
259
+ * │ │ └┴──────┘ ││ │
260
+ * │ │ ││ │
261
+ * │ │ ┌───────────────────────┼┼────┐ │
262
+ * │ │ │ Surface ││ │ │
263
+ * │ │ │ ││ │ │
264
+ * │ │ │ ││ │ │
265
+ * │ │ │ ││ │ │
266
+ * │ │ │ ││ │ │
267
+ * │ ┌┼─────┼───────────────────────┼│ │ │
268
+ * │ ┌─►┴──────┼────────────────────────┘ ├┐ │
269
+ * │ │ │ inline/blockOOBCorrection ││ │
270
+ * │ │ │ │ ││ │
271
+ * │ │ │ ├──►├│ │
272
+ * │ │ │ │ ││ │
273
+ * │ │ └────────────────────────┐▼───┼┘ │
274
+ * │ blockScrollbarHeight └────┘ │
275
+ * │ │
276
+ * └───────────────────────────────────────────────┘
277
+ */
278
+ // Calculate the block positioning properties
279
+ let {
280
+ blockInset,
281
+ blockOutOfBoundsCorrection,
282
+ surfaceBlockProperty
283
+ } = this.calculateBlock({
284
+ surfaceRect,
285
+ anchorRect,
286
+ anchorBlock,
287
+ surfaceBlock,
288
+ yOffset,
289
+ positioning,
290
+ windowInnerHeight,
291
+ blockScrollbarHeight
292
+ });
293
+ // If the surface should be out of bounds in the block direction, flip the
294
+ // surface and anchor corner block values and recalculate
295
+ if (blockOutOfBoundsCorrection && !disableBlockFlip) {
296
+ const flippedSurfaceBlock = surfaceBlock === 'start' ? 'end' : 'start';
297
+ const flippedAnchorBlock = anchorBlock === 'start' ? 'end' : 'start';
298
+ const flippedBlock = this.calculateBlock({
299
+ surfaceRect,
300
+ anchorRect,
301
+ anchorBlock: flippedAnchorBlock,
302
+ surfaceBlock: flippedSurfaceBlock,
303
+ yOffset,
304
+ positioning,
305
+ windowInnerHeight,
306
+ blockScrollbarHeight
307
+ });
308
+ // In the case that the flipped verion would require less out of bounds
309
+ // correcting, use the flipped corner block values
310
+ if (blockOutOfBoundsCorrection > flippedBlock.blockOutOfBoundsCorrection) {
311
+ blockInset = flippedBlock.blockInset;
312
+ blockOutOfBoundsCorrection = flippedBlock.blockOutOfBoundsCorrection;
313
+ surfaceBlockProperty = flippedBlock.surfaceBlockProperty;
314
+ }
315
+ }
316
+ // Calculate the inline positioning properties
317
+ let {
318
+ inlineInset,
319
+ inlineOutOfBoundsCorrection,
320
+ surfaceInlineProperty
321
+ } = this.calculateInline({
322
+ surfaceRect,
323
+ anchorRect,
324
+ anchorInline,
325
+ surfaceInline,
326
+ xOffset,
327
+ positioning,
328
+ isLTR,
329
+ windowInnerWidth,
330
+ inlineScrollbarWidth
331
+ });
332
+ // If the surface should be out of bounds in the inline direction, flip the
333
+ // surface and anchor corner inline values and recalculate
334
+ if (inlineOutOfBoundsCorrection && !disableInlineFlip) {
335
+ const flippedSurfaceInline = surfaceInline === 'start' ? 'end' : 'start';
336
+ const flippedAnchorInline = anchorInline === 'start' ? 'end' : 'start';
337
+ const flippedInline = this.calculateInline({
338
+ surfaceRect,
339
+ anchorRect,
340
+ anchorInline: flippedAnchorInline,
341
+ surfaceInline: flippedSurfaceInline,
342
+ xOffset,
343
+ positioning,
344
+ isLTR,
345
+ windowInnerWidth,
346
+ inlineScrollbarWidth
347
+ });
348
+ // In the case that the flipped verion would require less out of bounds
349
+ // correcting, use the flipped corner inline values
350
+ if (Math.abs(inlineOutOfBoundsCorrection) > Math.abs(flippedInline.inlineOutOfBoundsCorrection)) {
351
+ inlineInset = flippedInline.inlineInset;
352
+ inlineOutOfBoundsCorrection = flippedInline.inlineOutOfBoundsCorrection;
353
+ surfaceInlineProperty = flippedInline.surfaceInlineProperty;
354
+ }
355
+ }
356
+ // If we are simply repositioning the surface back inside the viewport,
357
+ // subtract the out of bounds correction values from the positioning.
358
+ if (repositionStrategy === 'move') {
359
+ blockInset = blockInset - blockOutOfBoundsCorrection;
360
+ inlineInset = inlineInset - inlineOutOfBoundsCorrection;
361
+ }
362
+ this.surfaceStylesInternal = {
363
+ 'display': 'block',
364
+ 'opacity': '1',
365
+ [surfaceBlockProperty]: `${blockInset}px`,
366
+ [surfaceInlineProperty]: `${inlineInset}px`
367
+ };
368
+ // In the case that we are resizing the surface to stay inside the viewport
369
+ // we need to set height and width on the surface.
370
+ if (repositionStrategy === 'resize') {
371
+ // Add a height property to the styles if there is block height correction
372
+ if (blockOutOfBoundsCorrection) {
373
+ this.surfaceStylesInternal['height'] = `${surfaceRect.height - blockOutOfBoundsCorrection}px`;
374
+ }
375
+ // Add a width property to the styles if there is block height correction
376
+ if (inlineOutOfBoundsCorrection) {
377
+ this.surfaceStylesInternal['width'] = `${surfaceRect.width - inlineOutOfBoundsCorrection}px`;
378
+ }
379
+ }
380
+ this.host.requestUpdate();
381
+ }
382
+ /**
383
+ * Calculates the css property, the inset, and the out of bounds correction
384
+ * for the surface in the block direction.
385
+ */
386
+ calculateBlock(config) {
387
+ const {
388
+ surfaceRect,
389
+ anchorRect,
390
+ anchorBlock,
391
+ surfaceBlock,
392
+ yOffset,
393
+ positioning,
394
+ windowInnerHeight,
395
+ blockScrollbarHeight
396
+ } = config;
397
+ // We use number booleans to multiply values rather than `if` / ternary
398
+ // statements because it _heavily_ cuts down on nesting and readability
399
+ const relativeToWindow = positioning === 'fixed' || positioning === 'document' ? 1 : 0;
400
+ const relativeToDocument = positioning === 'document' ? 1 : 0;
401
+ const isSurfaceBlockStart = surfaceBlock === 'start' ? 1 : 0;
402
+ const isSurfaceBlockEnd = surfaceBlock === 'end' ? 1 : 0;
403
+ const isOneBlockEnd = anchorBlock !== surfaceBlock ? 1 : 0;
404
+ // Whether or not to apply the height of the anchor
405
+ const blockAnchorOffset = isOneBlockEnd * anchorRect.height + yOffset;
406
+ // The absolute block position of the anchor relative to window
407
+ const blockTopLayerOffset = isSurfaceBlockStart * anchorRect.top + isSurfaceBlockEnd * (windowInnerHeight - anchorRect.bottom - blockScrollbarHeight);
408
+ const blockDocumentOffset = isSurfaceBlockStart * window.scrollY - isSurfaceBlockEnd * window.scrollY;
409
+ // If the surface's block would be out of bounds of the window, move it back
410
+ // in
411
+ const blockOutOfBoundsCorrection = Math.abs(Math.min(0, windowInnerHeight - blockTopLayerOffset - blockAnchorOffset - surfaceRect.height));
412
+ // The block logical value of the surface
413
+ const blockInset = relativeToWindow * blockTopLayerOffset + relativeToDocument * blockDocumentOffset + blockAnchorOffset;
414
+ const surfaceBlockProperty = surfaceBlock === 'start' ? 'inset-block-start' : 'inset-block-end';
415
+ return {
416
+ blockInset,
417
+ blockOutOfBoundsCorrection,
418
+ surfaceBlockProperty
419
+ };
420
+ }
421
+ /**
422
+ * Calculates the css property, the inset, and the out of bounds correction
423
+ * for the surface in the inline direction.
424
+ */
425
+ calculateInline(config) {
426
+ const {
427
+ isLTR: isLTRBool,
428
+ surfaceInline,
429
+ anchorInline,
430
+ anchorRect,
431
+ surfaceRect,
432
+ xOffset,
433
+ positioning,
434
+ windowInnerWidth,
435
+ inlineScrollbarWidth
436
+ } = config;
437
+ // We use number booleans to multiply values rather than `if` / ternary
438
+ // statements because it _heavily_ cuts down on nesting and readability
439
+ const relativeToWindow = positioning === 'fixed' || positioning === 'document' ? 1 : 0;
440
+ const relativeToDocument = positioning === 'document' ? 1 : 0;
441
+ const isLTR = isLTRBool ? 1 : 0;
442
+ const isRTL = isLTRBool ? 0 : 1;
443
+ const isSurfaceInlineStart = surfaceInline === 'start' ? 1 : 0;
444
+ const isSurfaceInlineEnd = surfaceInline === 'end' ? 1 : 0;
445
+ const isOneInlineEnd = anchorInline !== surfaceInline ? 1 : 0;
446
+ // Whether or not to apply the width of the anchor
447
+ const inlineAnchorOffset = isOneInlineEnd * anchorRect.width + xOffset;
448
+ // The inline position of the anchor relative to window in LTR
449
+ const inlineTopLayerOffsetLTR = isSurfaceInlineStart * anchorRect.left + isSurfaceInlineEnd * (windowInnerWidth - anchorRect.right - inlineScrollbarWidth);
450
+ // The inline position of the anchor relative to window in RTL
451
+ const inlineTopLayerOffsetRTL = isSurfaceInlineStart * (windowInnerWidth - anchorRect.right - inlineScrollbarWidth) + isSurfaceInlineEnd * anchorRect.left;
452
+ // The inline position of the anchor relative to window
453
+ const inlineTopLayerOffset = isLTR * inlineTopLayerOffsetLTR + isRTL * inlineTopLayerOffsetRTL;
454
+ // The inline position of the anchor relative to window in LTR
455
+ const inlineDocumentOffsetLTR = isSurfaceInlineStart * window.scrollX - isSurfaceInlineEnd * window.scrollX;
456
+ // The inline position of the anchor relative to window in RTL
457
+ const inlineDocumentOffsetRTL = isSurfaceInlineEnd * window.scrollX - isSurfaceInlineStart * window.scrollX;
458
+ // The inline position of the anchor relative to window
459
+ const inlineDocumentOffset = isLTR * inlineDocumentOffsetLTR + isRTL * inlineDocumentOffsetRTL;
460
+ // If the surface's inline would be out of bounds of the window, move it
461
+ // back in
462
+ const inlineOutOfBoundsCorrection = Math.abs(Math.min(0, windowInnerWidth - inlineTopLayerOffset - inlineAnchorOffset - surfaceRect.width));
463
+ // The inline logical value of the surface
464
+ const inlineInset = relativeToWindow * inlineTopLayerOffset + inlineAnchorOffset + relativeToDocument * inlineDocumentOffset;
465
+ let surfaceInlineProperty = surfaceInline === 'start' ? 'inset-inline-start' : 'inset-inline-end';
466
+ // There are cases where the element is RTL but the root of the page is not.
467
+ // In these cases we want to not use logical properties.
468
+ if (positioning === 'document' || positioning === 'fixed') {
469
+ if (surfaceInline === 'start' && isLTRBool || surfaceInline === 'end' && !isLTRBool) {
470
+ surfaceInlineProperty = 'left';
471
+ } else {
472
+ surfaceInlineProperty = 'right';
473
+ }
474
+ }
475
+ return {
476
+ inlineInset,
477
+ inlineOutOfBoundsCorrection,
478
+ surfaceInlineProperty
479
+ };
480
+ }
481
+ hostUpdate() {
482
+ this.onUpdate();
483
+ }
484
+ hostUpdated() {
485
+ this.onUpdate();
486
+ }
487
+ /**
488
+ * Checks whether the properties passed into the controller have changed since
489
+ * the last positioning. If so, it will reposition if the surface is open or
490
+ * close it if the surface should close.
491
+ */
492
+ async onUpdate() {
493
+ const props = this.getProperties();
494
+ let hasChanged = false;
495
+ for (const [key, value] of Object.entries(props)) {
496
+ // tslint:disable-next-line
497
+ hasChanged = hasChanged || value !== this.lastValues[key];
498
+ if (hasChanged) break;
499
+ }
500
+ const openChanged = this.lastValues.isOpen !== props.isOpen;
501
+ const hasAnchor = !!props.anchorEl;
502
+ const hasSurface = !!props.surfaceEl;
503
+ if (hasChanged && hasAnchor && hasSurface) {
504
+ // Only update isOpen, because if it's closed, we do not want to waste
505
+ // time on a useless reposition calculation. So save the other "dirty"
506
+ // values until next time it opens.
507
+ this.lastValues.isOpen = props.isOpen;
508
+ if (props.isOpen) {
509
+ // We are going to do a reposition, so save the prop values for future
510
+ // dirty checking.
511
+ this.lastValues = props;
512
+ await this.position();
513
+ props.onOpen();
514
+ } else if (openChanged) {
515
+ await props.beforeClose();
516
+ this.close();
517
+ props.onClose();
518
+ }
519
+ }
520
+ }
521
+ /**
522
+ * Hides the surface.
523
+ */
524
+ close() {
525
+ this.surfaceStylesInternal = {
526
+ 'display': 'none'
527
+ };
528
+ this.host.requestUpdate();
529
+ const surfaceEl = this.getProperties().surfaceEl;
530
+ // The following type casts are required due to differing TS types in Google
531
+ // and open source.
532
+ if (surfaceEl !== null && surfaceEl !== void 0 && surfaceEl.popover && surfaceEl !== null && surfaceEl !== void 0 && surfaceEl.isConnected) {
533
+ surfaceEl.hidePopover();
534
+ }
535
+ }
536
+ }
537
+
538
+ /**
539
+ * @license
540
+ * Copyright 2023 Google LLC
541
+ * SPDX-License-Identifier: Apache-2.0
542
+ */
543
+ /**
544
+ * Indicies to access the TypeaheadRecord tuple type.
545
+ */
546
+ const TYPEAHEAD_RECORD = {
547
+ INDEX: 0,
548
+ ITEM: 1,
549
+ TEXT: 2
550
+ };
551
+ /**
552
+ * This controller listens to `keydown` events and searches the header text of
553
+ * an array of `MenuItem`s with the corresponding entered keys within the buffer
554
+ * time and activates the item.
555
+ *
556
+ * @example
557
+ * ```ts
558
+ * const typeaheadController = new TypeaheadController(() => ({
559
+ * typeaheadBufferTime: 50,
560
+ * getItems: () => Array.from(document.querySelectorAll('md-menu-item'))
561
+ * }));
562
+ * html`
563
+ * <div
564
+ * @keydown=${typeaheadController.onKeydown}
565
+ * tabindex="0"
566
+ * class="activeItemText">
567
+ * <!-- focusable element that will receive keydown events -->
568
+ * Apple
569
+ * </div>
570
+ * <div>
571
+ * <md-menu-item active header="Apple"></md-menu-item>
572
+ * <md-menu-item header="Apricot"></md-menu-item>
573
+ * <md-menu-item header="Banana"></md-menu-item>
574
+ * <md-menu-item header="Olive"></md-menu-item>
575
+ * <md-menu-item header="Orange"></md-menu-item>
576
+ * </div>
577
+ * `;
578
+ * ```
579
+ */
580
+ class TypeaheadController {
581
+ /**
582
+ * @param getProperties A function that returns the options of the typeahead
583
+ * controller:
584
+ *
585
+ * {
586
+ * getItems: A function that returns an array of menu items to be searched.
587
+ * typeaheadBufferTime: The maximum time between each keystroke to keep the
588
+ * current type buffer alive.
589
+ * }
590
+ */
591
+ constructor(getProperties) {
592
+ this.getProperties = getProperties;
593
+ /**
594
+ * Array of tuples that helps with indexing.
595
+ */
596
+ this.typeaheadRecords = [];
597
+ /**
598
+ * Currently-typed text since last buffer timeout
599
+ */
600
+ this.typaheadBuffer = '';
601
+ /**
602
+ * The timeout id from the current buffer's setTimeout
603
+ */
604
+ this.cancelTypeaheadTimeout = 0;
605
+ /**
606
+ * If we are currently "typing"
607
+ */
608
+ this.isTypingAhead = false;
609
+ /**
610
+ * The record of the last active item.
611
+ */
612
+ this.lastActiveRecord = null;
613
+ /**
614
+ * Apply this listener to the element that will receive `keydown` events that
615
+ * should trigger this controller.
616
+ *
617
+ * @param event The native browser `KeyboardEvent` from the `keydown` event.
618
+ */
619
+ this.onKeydown = event => {
620
+ if (this.isTypingAhead) {
621
+ this.typeahead(event);
622
+ } else {
623
+ this.beginTypeahead(event);
624
+ }
625
+ };
626
+ /**
627
+ * Ends the current typeahead and clears the buffer.
628
+ */
629
+ this.endTypeahead = () => {
630
+ this.isTypingAhead = false;
631
+ this.typaheadBuffer = '';
632
+ this.typeaheadRecords = [];
633
+ };
634
+ }
635
+ get items() {
636
+ return this.getProperties().getItems();
637
+ }
638
+ get active() {
639
+ return this.getProperties().active;
640
+ }
641
+ /**
642
+ * Sets up typingahead
643
+ */
644
+ beginTypeahead(event) {
645
+ if (!this.active) {
646
+ return;
647
+ }
648
+ // We don't want to typeahead if the _beginning_ of the typeahead is a menu
649
+ // navigation, or a selection. We will handle "Space" only if it's in the
650
+ // middle of a typeahead
651
+ if (event.code === 'Space' || event.code === 'Enter' || event.code.startsWith('Arrow') || event.code === 'Escape') {
652
+ return;
653
+ }
654
+ this.isTypingAhead = true;
655
+ // Generates the record array data structure which is the index, the element
656
+ // and a normalized header.
657
+ this.typeaheadRecords = this.items.map((el, index) => [index, el, el.typeaheadText.trim().toLowerCase()]);
658
+ this.lastActiveRecord = this.typeaheadRecords.find(record => record[TYPEAHEAD_RECORD.ITEM].tabIndex === 0) ?? null;
659
+ if (this.lastActiveRecord) {
660
+ this.lastActiveRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = -1;
661
+ }
662
+ this.typeahead(event);
663
+ }
664
+ /**
665
+ * Performs the typeahead. Based on the normalized items and the current text
666
+ * buffer, finds the _next_ item with matching text and activates it.
667
+ *
668
+ * @example
669
+ *
670
+ * items: Apple, Banana, Olive, Orange, Cucumber
671
+ * buffer: ''
672
+ * user types: o
673
+ *
674
+ * activates Olive
675
+ *
676
+ * @example
677
+ *
678
+ * items: Apple, Banana, Olive (active), Orange, Cucumber
679
+ * buffer: 'o'
680
+ * user types: l
681
+ *
682
+ * activates Olive
683
+ *
684
+ * @example
685
+ *
686
+ * items: Apple, Banana, Olive (active), Orange, Cucumber
687
+ * buffer: ''
688
+ * user types: o
689
+ *
690
+ * activates Orange
691
+ *
692
+ * @example
693
+ *
694
+ * items: Apple, Banana, Olive, Orange (active), Cucumber
695
+ * buffer: ''
696
+ * user types: o
697
+ *
698
+ * activates Olive
699
+ */
700
+ typeahead(event) {
701
+ if (event.defaultPrevented) return;
702
+ clearTimeout(this.cancelTypeaheadTimeout);
703
+ // Stop typingahead if one of the navigation or selection keys (except for
704
+ // Space) are pressed
705
+ if (event.code === 'Enter' || event.code.startsWith('Arrow') || event.code === 'Escape') {
706
+ this.endTypeahead();
707
+ if (this.lastActiveRecord) {
708
+ this.lastActiveRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = -1;
709
+ }
710
+ return;
711
+ }
712
+ // If Space is pressed, prevent it from selecting and closing the menu
713
+ if (event.code === 'Space') {
714
+ event.preventDefault();
715
+ }
716
+ // Start up a new keystroke buffer timeout
717
+ this.cancelTypeaheadTimeout = setTimeout(this.endTypeahead, this.getProperties().typeaheadBufferTime);
718
+ this.typaheadBuffer += event.key.toLowerCase();
719
+ const lastActiveIndex = this.lastActiveRecord ? this.lastActiveRecord[TYPEAHEAD_RECORD.INDEX] : -1;
720
+ const numRecords = this.typeaheadRecords.length;
721
+ /**
722
+ * Sorting function that will resort the items starting with the given index
723
+ *
724
+ * @example
725
+ *
726
+ * this.typeaheadRecords =
727
+ * 0: [0, <reference>, 'apple']
728
+ * 1: [1, <reference>, 'apricot']
729
+ * 2: [2, <reference>, 'banana']
730
+ * 3: [3, <reference>, 'olive'] <-- lastActiveIndex
731
+ * 4: [4, <reference>, 'orange']
732
+ * 5: [5, <reference>, 'strawberry']
733
+ *
734
+ * this.typeaheadRecords.sort((a,b) => rebaseIndexOnActive(a)
735
+ * - rebaseIndexOnActive(b)) ===
736
+ * 0: [3, <reference>, 'olive'] <-- lastActiveIndex
737
+ * 1: [4, <reference>, 'orange']
738
+ * 2: [5, <reference>, 'strawberry']
739
+ * 3: [0, <reference>, 'apple']
740
+ * 4: [1, <reference>, 'apricot']
741
+ * 5: [2, <reference>, 'banana']
742
+ */
743
+ const rebaseIndexOnActive = record => {
744
+ return (record[TYPEAHEAD_RECORD.INDEX] + numRecords - lastActiveIndex) % numRecords;
745
+ };
746
+ // records filtered and sorted / rebased around the last active index
747
+ 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));
748
+ // Just leave if there's nothing that matches. Native select will just
749
+ // choose the first thing that starts with the next letter in the alphabet
750
+ // but that's out of scope and hard to localize
751
+ if (matchingRecords.length === 0) {
752
+ clearTimeout(this.cancelTypeaheadTimeout);
753
+ if (this.lastActiveRecord) {
754
+ this.lastActiveRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = -1;
755
+ }
756
+ this.endTypeahead();
757
+ return;
758
+ }
759
+ const isNewQuery = this.typaheadBuffer.length === 1;
760
+ let nextRecord;
761
+ // This is likely the case that someone is trying to "tab" through different
762
+ // entries that start with the same letter
763
+ if (this.lastActiveRecord === matchingRecords[0] && isNewQuery) {
764
+ nextRecord = matchingRecords[1] ?? matchingRecords[0];
765
+ } else {
766
+ nextRecord = matchingRecords[0];
767
+ }
768
+ if (this.lastActiveRecord) {
769
+ this.lastActiveRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = -1;
770
+ }
771
+ this.lastActiveRecord = nextRecord;
772
+ nextRecord[TYPEAHEAD_RECORD.ITEM].tabIndex = 0;
773
+ nextRecord[TYPEAHEAD_RECORD.ITEM].focus();
774
+ return;
775
+ }
776
+ }
777
+
778
+ /**
779
+ * @license
780
+ * Copyright 2023 Google LLC
781
+ * SPDX-License-Identifier: Apache-2.0
782
+ */
783
+ /**
784
+ * The default value for the typeahead buffer time in Milliseconds.
785
+ */
786
+ const DEFAULT_TYPEAHEAD_BUFFER_TIME = 200;
787
+ const submenuNavKeys = new Set([NavigableKeys.ArrowDown, NavigableKeys.ArrowUp, NavigableKeys.Home, NavigableKeys.End]);
788
+ const menuNavKeys = new Set([NavigableKeys.ArrowLeft, NavigableKeys.ArrowRight, ...submenuNavKeys]);
789
+ /**
790
+ * Gets the currently focused element on the page.
791
+ *
792
+ * @param activeDoc The document or shadowroot from which to start the search.
793
+ * Defaults to `window.document`
794
+ * @return Returns the currently deeply focused element or `null` if none.
795
+ */
796
+ function getFocusedElement(activeDoc = document) {
797
+ let activeEl = activeDoc.activeElement;
798
+ // Check for activeElement in the case that an element with a shadow root host
799
+ // is currently focused.
800
+ while (activeEl && (_activeEl = activeEl) !== null && _activeEl !== void 0 && (_activeEl = _activeEl.shadowRoot) !== null && _activeEl !== void 0 && _activeEl.activeElement) {
801
+ var _activeEl;
802
+ activeEl = activeEl.shadowRoot.activeElement;
803
+ }
804
+ return activeEl;
805
+ }
806
+ /**
807
+ * @fires opening {Event} Fired before the opening animation begins
808
+ * @fires opened {Event} Fired once the menu is open, after any animations
809
+ * @fires closing {Event} Fired before the closing animation begins
810
+ * @fires closed {Event} Fired once the menu is closed, after any animations
811
+ */
812
+ class Menu extends i {
813
+ /**
814
+ * Whether the menu is animating upwards or downwards when opening. This is
815
+ * helpful for calculating some animation calculations.
816
+ */
817
+ get openDirection() {
818
+ const menuCornerBlock = this.menuCorner.split('-')[0];
819
+ return menuCornerBlock === 'start' ? 'DOWN' : 'UP';
820
+ }
821
+ /**
822
+ * The element which the menu should align to. If `anchor` is set to a
823
+ * non-empty idref string, then `anchorEl` will resolve to the element with
824
+ * the given id in the same root node. Otherwise, `null`.
825
+ */
826
+ get anchorElement() {
827
+ if (this.anchor) {
828
+ return this.getRootNode().querySelector(`#${this.anchor}`);
829
+ }
830
+ return this.currentAnchorElement;
831
+ }
832
+ set anchorElement(element) {
833
+ this.currentAnchorElement = element;
834
+ this.requestUpdate('anchorElement');
835
+ }
836
+ constructor() {
837
+ super();
838
+ /**
839
+ * The ID of the element in the same root node in which the menu should align
840
+ * to. Overrides setting `anchorElement = elementReference`.
841
+ *
842
+ * __NOTE__: anchor or anchorElement must either be an HTMLElement or resolve
843
+ * to an HTMLElement in order for menu to open.
844
+ */
845
+ this.anchor = '';
846
+ /**
847
+ * Whether the positioning algorithm should calculate relative to the parent
848
+ * of the anchor element (`absolute`), relative to the window (`fixed`), or
849
+ * relative to the document (`document`). `popover` will use the popover API
850
+ * to render the menu in the top-layer. If your browser does not support the
851
+ * popover API, it will fall back to `fixed`.
852
+ *
853
+ * __Examples for `position = 'fixed'`:__
854
+ *
855
+ * - If there is no `position:relative` in the given parent tree and the
856
+ * surface is `position:absolute`
857
+ * - If the surface is `position:fixed`
858
+ * - If the surface is in the "top layer"
859
+ * - The anchor and the surface do not share a common `position:relative`
860
+ * ancestor
861
+ *
862
+ * When using `positioning=fixed`, in most cases, the menu should position
863
+ * itself above most other `position:absolute` or `position:fixed` elements
864
+ * when placed inside of them. e.g. using a menu inside of an `md-dialog`.
865
+ *
866
+ * __NOTE__: Fixed menus will not scroll with the page and will be fixed to
867
+ * the window instead.
868
+ *
869
+ * __Examples for `position = 'document'`:__
870
+ *
871
+ * - There is no parent that creates a relative positioning context e.g.
872
+ * `position: relative`, `position: absolute`, `transform: translate(x, y)`,
873
+ * etc.
874
+ * - You put the effort into hoisting the menu to the top of the DOM like the
875
+ * end of the `<body>` to render over everything or in a top-layer.
876
+ * - You are reusing a single `md-menu` element that dynamically renders
877
+ * content.
878
+ *
879
+ * __Examples for `position = 'popover'`:__
880
+ *
881
+ * - Your browser supports `popover`.
882
+ * - Most cases. Once popover is in browsers, this will become the default.
883
+ */
884
+ this.positioning = 'absolute';
885
+ /**
886
+ * Skips the opening and closing animations.
887
+ */
888
+ this.quick = false;
889
+ /**
890
+ * Displays overflow content like a submenu. Not required in most cases when
891
+ * using `positioning="popover"`.
892
+ *
893
+ * __NOTE__: This may cause adverse effects if you set
894
+ * `md-menu {max-height:...}`
895
+ * and have items overflowing items in the "y" direction.
896
+ */
897
+ this.hasOverflow = false;
898
+ /**
899
+ * Opens the menu and makes it visible. Alternative to the `.show()` and
900
+ * `.close()` methods
901
+ */
902
+ this.open = false;
903
+ /**
904
+ * Offsets the menu's inline alignment from the anchor by the given number in
905
+ * pixels. This value is direction aware and will follow the LTR / RTL
906
+ * direction.
907
+ *
908
+ * e.g. LTR: positive -> right, negative -> left
909
+ * RTL: positive -> left, negative -> right
910
+ */
911
+ this.xOffset = 0;
912
+ /**
913
+ * Offsets the menu's block alignment from the anchor by the given number in
914
+ * pixels.
915
+ *
916
+ * e.g. positive -> down, negative -> up
917
+ */
918
+ this.yOffset = 0;
919
+ /**
920
+ * Disable the `flip` behavior that usually happens on the horizontal axis
921
+ * when the surface would render outside the viewport.
922
+ */
923
+ this.noHorizontalFlip = false;
924
+ /**
925
+ * Disable the `flip` behavior that usually happens on the vertical axis when
926
+ * the surface would render outside the viewport.
927
+ */
928
+ this.noVerticalFlip = false;
929
+ /**
930
+ * The max time between the keystrokes of the typeahead menu behavior before
931
+ * it clears the typeahead buffer.
932
+ */
933
+ this.typeaheadDelay = DEFAULT_TYPEAHEAD_BUFFER_TIME;
934
+ /**
935
+ * The corner of the anchor which to align the menu in the standard logical
936
+ * property style of <block>-<inline> e.g. `'end-start'`.
937
+ *
938
+ * NOTE: This value may not be respected by the menu positioning algorithm
939
+ * if the menu would render outisde the viewport.
940
+ * Use `no-horizontal-flip` or `no-vertical-flip` to force the usage of the value
941
+ */
942
+ this.anchorCorner = Corner.END_START;
943
+ /**
944
+ * The corner of the menu which to align the anchor in the standard logical
945
+ * property style of <block>-<inline> e.g. `'start-start'`.
946
+ *
947
+ * NOTE: This value may not be respected by the menu positioning algorithm
948
+ * if the menu would render outisde the viewport.
949
+ * Use `no-horizontal-flip` or `no-vertical-flip` to force the usage of the value
950
+ */
951
+ this.menuCorner = Corner.START_START;
952
+ /**
953
+ * Keeps the user clicks outside the menu.
954
+ *
955
+ * NOTE: clicking outside may still cause focusout to close the menu so see
956
+ * `stayOpenOnFocusout`.
957
+ */
958
+ this.stayOpenOnOutsideClick = false;
959
+ /**
960
+ * Keeps the menu open when focus leaves the menu's composed subtree.
961
+ *
962
+ * NOTE: Focusout behavior will stop propagation of the focusout event. Set
963
+ * this property to true to opt-out of menu's focusout handling altogether.
964
+ */
965
+ this.stayOpenOnFocusout = false;
966
+ /**
967
+ * After closing, does not restore focus to the last focused element before
968
+ * the menu was opened.
969
+ */
970
+ this.skipRestoreFocus = false;
971
+ /**
972
+ * The element that should be focused by default once opened.
973
+ *
974
+ * NOTE: When setting default focus to 'LIST_ROOT', remember to change
975
+ * `tabindex` to `0` and change md-menu's display to something other than
976
+ * `display: contents` when necessary.
977
+ */
978
+ this.defaultFocus = FocusState.FIRST_ITEM;
979
+ /**
980
+ * Turns off navigation wrapping. By default, navigating past the end of the
981
+ * menu items will wrap focus back to the beginning and vice versa. Use this
982
+ * for ARIA patterns that do not wrap focus, like combobox.
983
+ */
984
+ this.noNavigationWrap = false;
985
+ this.typeaheadActive = true;
986
+ /**
987
+ * Whether or not the current menu is a submenu and should not handle specific
988
+ * navigation keys.
989
+ *
990
+ * @export
991
+ */
992
+ this.isSubmenu = false;
993
+ /**
994
+ * The event path of the last window pointerdown event.
995
+ */
996
+ this.pointerPath = [];
997
+ /**
998
+ * Whether or not the menu is repositoining due to window / document resize
999
+ */
1000
+ this.isRepositioning = false;
1001
+ this.openCloseAnimationSignal = createAnimationSignal();
1002
+ this.listController = new ListController({
1003
+ isItem: maybeItem => {
1004
+ return maybeItem.hasAttribute('md-menu-item');
1005
+ },
1006
+ getPossibleItems: () => this.slotItems,
1007
+ isRtl: () => getComputedStyle(this).direction === 'rtl',
1008
+ deactivateItem: item => {
1009
+ item.selected = false;
1010
+ item.tabIndex = -1;
1011
+ },
1012
+ activateItem: item => {
1013
+ item.selected = true;
1014
+ item.tabIndex = 0;
1015
+ },
1016
+ isNavigableKey: key => {
1017
+ if (!this.isSubmenu) {
1018
+ return menuNavKeys.has(key);
1019
+ }
1020
+ const isRtl = getComputedStyle(this).direction === 'rtl';
1021
+ // we want md-submenu to handle the submenu's left/right arrow exit
1022
+ // key so it can close the menu instead of navigate the list.
1023
+ // Therefore we need to include all keys but left/right arrow close
1024
+ // key
1025
+ const arrowOpen = isRtl ? NavigableKeys.ArrowLeft : NavigableKeys.ArrowRight;
1026
+ if (key === arrowOpen) {
1027
+ return true;
1028
+ }
1029
+ return submenuNavKeys.has(key);
1030
+ },
1031
+ wrapNavigation: () => !this.noNavigationWrap
1032
+ });
1033
+ /**
1034
+ * The element that was focused before the menu opened.
1035
+ */
1036
+ this.lastFocusedElement = null;
1037
+ /**
1038
+ * Handles typeahead navigation through the menu.
1039
+ */
1040
+ this.typeaheadController = new TypeaheadController(() => {
1041
+ return {
1042
+ getItems: () => this.items,
1043
+ typeaheadBufferTime: this.typeaheadDelay,
1044
+ active: this.typeaheadActive
1045
+ };
1046
+ });
1047
+ this.currentAnchorElement = null;
1048
+ this.internals =
1049
+ // Cast needed for closure
1050
+ this.attachInternals();
1051
+ /**
1052
+ * Handles positioning the surface and aligning it to the anchor as well as
1053
+ * keeping it in the viewport.
1054
+ */
1055
+ this.menuPositionController = new SurfacePositionController(this, () => {
1056
+ return {
1057
+ anchorCorner: this.anchorCorner,
1058
+ surfaceCorner: this.menuCorner,
1059
+ surfaceEl: this.surfaceEl,
1060
+ anchorEl: this.anchorElement,
1061
+ positioning: this.positioning === 'popover' ? 'document' : this.positioning,
1062
+ isOpen: this.open,
1063
+ xOffset: this.xOffset,
1064
+ yOffset: this.yOffset,
1065
+ disableBlockFlip: this.noVerticalFlip,
1066
+ disableInlineFlip: this.noHorizontalFlip,
1067
+ onOpen: this.onOpened,
1068
+ beforeClose: this.beforeClose,
1069
+ onClose: this.onClosed,
1070
+ // We can't resize components that have overflow like menus with
1071
+ // submenus because the overflow-y will show menu items / content
1072
+ // outside the bounds of the menu. Popover API fixes this because each
1073
+ // submenu is hoisted to the top-layer and are not considered overflow
1074
+ // content.
1075
+ repositionStrategy: this.hasOverflow && this.positioning !== 'popover' ? 'move' : 'resize'
1076
+ };
1077
+ });
1078
+ this.onWindowResize = () => {
1079
+ if (this.isRepositioning || this.positioning !== 'document' && this.positioning !== 'fixed' && this.positioning !== 'popover') {
1080
+ return;
1081
+ }
1082
+ this.isRepositioning = true;
1083
+ this.reposition();
1084
+ this.isRepositioning = false;
1085
+ };
1086
+ this.handleFocusout = async event => {
1087
+ const anchorEl = this.anchorElement;
1088
+ // Do not close if we focused out by clicking on the anchor element. We
1089
+ // can't assume anchor buttons can be the related target because of iOS does
1090
+ // not focus buttons.
1091
+ if (this.stayOpenOnFocusout || !this.open || this.pointerPath.includes(anchorEl)) {
1092
+ return;
1093
+ }
1094
+ if (event.relatedTarget) {
1095
+ // Don't close the menu if we are switching focus between menu,
1096
+ // md-menu-item, and md-list or if the anchor was click focused, but check
1097
+ // if length of pointerPath is 0 because that means something was at least
1098
+ // clicked (shift+tab case).
1099
+ if (isElementInSubtree(event.relatedTarget, this) || this.pointerPath.length !== 0 && isElementInSubtree(event.relatedTarget, anchorEl)) {
1100
+ return;
1101
+ }
1102
+ } else if (this.pointerPath.includes(this)) {
1103
+ // If menu tabindex == -1 and the user clicks on the menu or a divider, we
1104
+ // want to keep the menu open.
1105
+ return;
1106
+ }
1107
+ const oldRestoreFocus = this.skipRestoreFocus;
1108
+ // allow focus to continue to the next focused object rather than returning
1109
+ this.skipRestoreFocus = true;
1110
+ this.close();
1111
+ // await for close
1112
+ await this.updateComplete;
1113
+ // return to previous behavior
1114
+ this.skipRestoreFocus = oldRestoreFocus;
1115
+ };
1116
+ /**
1117
+ * Saves the last focused element focuses the new element based on
1118
+ * `defaultFocus`, and animates open.
1119
+ */
1120
+ this.onOpened = async () => {
1121
+ this.lastFocusedElement = getFocusedElement();
1122
+ const items = this.items;
1123
+ const activeItemRecord = getActiveItem(items);
1124
+ if (activeItemRecord && this.defaultFocus !== FocusState.NONE) {
1125
+ activeItemRecord.item.tabIndex = -1;
1126
+ }
1127
+ let animationAborted = !this.quick;
1128
+ if (this.quick) {
1129
+ this.dispatchEvent(new Event('opening'));
1130
+ } else {
1131
+ animationAborted = !!(await this.animateOpen());
1132
+ }
1133
+ // This must come after the opening animation or else it may focus one of
1134
+ // the items before the animation has begun and causes the list to slide
1135
+ // (block-padding-of-the-menu)px at the end of the animation
1136
+ switch (this.defaultFocus) {
1137
+ case FocusState.FIRST_ITEM:
1138
+ const first = getFirstActivatableItem(items);
1139
+ if (first) {
1140
+ first.tabIndex = 0;
1141
+ first.focus();
1142
+ await first.updateComplete;
1143
+ }
1144
+ break;
1145
+ case FocusState.LAST_ITEM:
1146
+ const last = getLastActivatableItem(items);
1147
+ if (last) {
1148
+ last.tabIndex = 0;
1149
+ last.focus();
1150
+ await last.updateComplete;
1151
+ }
1152
+ break;
1153
+ case FocusState.LIST_ROOT:
1154
+ this.focus();
1155
+ break;
1156
+ default:
1157
+ case FocusState.NONE:
1158
+ // Do nothing.
1159
+ break;
1160
+ }
1161
+ if (!animationAborted) {
1162
+ this.dispatchEvent(new Event('opened'));
1163
+ }
1164
+ };
1165
+ /**
1166
+ * Animates closed.
1167
+ */
1168
+ this.beforeClose = async () => {
1169
+ this.open = false;
1170
+ if (!this.skipRestoreFocus) {
1171
+ var _this$lastFocusedElem, _this$lastFocusedElem2;
1172
+ (_this$lastFocusedElem = this.lastFocusedElement) === null || _this$lastFocusedElem === void 0 || (_this$lastFocusedElem2 = _this$lastFocusedElem.focus) === null || _this$lastFocusedElem2 === void 0 || _this$lastFocusedElem2.call(_this$lastFocusedElem);
1173
+ }
1174
+ if (!this.quick) {
1175
+ await this.animateClose();
1176
+ }
1177
+ };
1178
+ /**
1179
+ * Focuses the last focused element.
1180
+ */
1181
+ this.onClosed = () => {
1182
+ if (this.quick) {
1183
+ this.dispatchEvent(new Event('closing'));
1184
+ this.dispatchEvent(new Event('closed'));
1185
+ }
1186
+ };
1187
+ this.onWindowPointerdown = event => {
1188
+ this.pointerPath = event.composedPath();
1189
+ };
1190
+ /**
1191
+ * We cannot listen to window click because Safari on iOS will not bubble a
1192
+ * click event on window if the item clicked is not a "clickable" item such as
1193
+ * <body>
1194
+ */
1195
+ this.onDocumentClick = event => {
1196
+ if (!this.open) {
1197
+ return;
1198
+ }
1199
+ const path = event.composedPath();
1200
+ if (!this.stayOpenOnOutsideClick && !path.includes(this) && !path.includes(this.anchorElement)) {
1201
+ this.open = false;
1202
+ }
1203
+ };
1204
+ {
1205
+ this.internals.role = 'menu';
1206
+ this.addEventListener('keydown', this.handleKeydown);
1207
+ // Capture so that we can grab the event before it reaches the menu item
1208
+ // istelf. Specifically useful for the case where typeahead encounters a
1209
+ // space and we don't want the menu item to close the menu.
1210
+ this.addEventListener('keydown', this.captureKeydown, {
1211
+ capture: true
1212
+ });
1213
+ this.addEventListener('focusout', this.handleFocusout);
1214
+ }
1215
+ }
1216
+ /**
1217
+ * The menu items associated with this menu. The items must be `MenuItem`s and
1218
+ * have both the `md-menu-item` and `md-list-item` attributes.
1219
+ */
1220
+ get items() {
1221
+ return this.listController.items;
1222
+ }
1223
+ willUpdate(changed) {
1224
+ if (!changed.has('open')) {
1225
+ return;
1226
+ }
1227
+ if (this.open) {
1228
+ this.removeAttribute('aria-hidden');
1229
+ return;
1230
+ }
1231
+ this.setAttribute('aria-hidden', 'true');
1232
+ }
1233
+ update(changed) {
1234
+ if (changed.has('open')) {
1235
+ if (this.open) {
1236
+ this.setUpGlobalEventListeners();
1237
+ } else {
1238
+ this.cleanUpGlobalEventListeners();
1239
+ }
1240
+ }
1241
+ // Firefox does not support popover. Fall-back to using fixed.
1242
+ if (changed.has('positioning') && this.positioning === 'popover' &&
1243
+ // type required for Google JS conformance
1244
+ !this.showPopover) {
1245
+ this.positioning = 'fixed';
1246
+ }
1247
+ super.update(changed);
1248
+ }
1249
+ connectedCallback() {
1250
+ super.connectedCallback();
1251
+ if (this.open) {
1252
+ this.setUpGlobalEventListeners();
1253
+ }
1254
+ }
1255
+ disconnectedCallback() {
1256
+ super.disconnectedCallback();
1257
+ this.cleanUpGlobalEventListeners();
1258
+ }
1259
+ getBoundingClientRect() {
1260
+ if (!this.surfaceEl) {
1261
+ return super.getBoundingClientRect();
1262
+ }
1263
+ return this.surfaceEl.getBoundingClientRect();
1264
+ }
1265
+ getClientRects() {
1266
+ if (!this.surfaceEl) {
1267
+ return super.getClientRects();
1268
+ }
1269
+ return this.surfaceEl.getClientRects();
1270
+ }
1271
+ render() {
1272
+ return this.renderSurface();
1273
+ }
1274
+ /**
1275
+ * Renders the positionable surface element and its contents.
1276
+ */
1277
+ renderSurface() {
1278
+ return b`
1279
+ <div
1280
+ class="menu ${e$2(this.getSurfaceClasses())}"
1281
+ style=${o$1(this.menuPositionController.surfaceStyles)}
1282
+ popover=${this.positioning === 'popover' ? 'manual' : A}>
1283
+ ${this.renderElevation()}
1284
+ <div class="items">
1285
+ <div class="item-padding"> ${this.renderMenuItems()} </div>
1286
+ </div>
1287
+ </div>
1288
+ `;
1289
+ }
1290
+ /**
1291
+ * Renders the menu items' slot
1292
+ */
1293
+ renderMenuItems() {
1294
+ return b`<slot
1295
+ @close-menu=${this.onCloseMenu}
1296
+ @deactivate-items=${this.onDeactivateItems}
1297
+ @request-activation=${this.onRequestActivation}
1298
+ @deactivate-typeahead=${this.handleDeactivateTypeahead}
1299
+ @activate-typeahead=${this.handleActivateTypeahead}
1300
+ @stay-open-on-focusout=${this.handleStayOpenOnFocusout}
1301
+ @close-on-focusout=${this.handleCloseOnFocusout}
1302
+ @slotchange=${this.listController.onSlotchange}></slot>`;
1303
+ }
1304
+ /**
1305
+ * Renders the elevation component.
1306
+ */
1307
+ renderElevation() {
1308
+ return b`<md-elevation part="elevation"></md-elevation>`;
1309
+ }
1310
+ getSurfaceClasses() {
1311
+ return {
1312
+ open: this.open,
1313
+ fixed: this.positioning === 'fixed',
1314
+ 'has-overflow': this.hasOverflow
1315
+ };
1316
+ }
1317
+ captureKeydown(event) {
1318
+ if (event.target === this && !event.defaultPrevented && isClosableKey(event.code)) {
1319
+ event.preventDefault();
1320
+ this.close();
1321
+ }
1322
+ this.typeaheadController.onKeydown(event);
1323
+ }
1324
+ /**
1325
+ * Performs the opening animation:
1326
+ *
1327
+ * https://direct.googleplex.com/#/spec/295000003+271060003
1328
+ *
1329
+ * @return A promise that resolve to `true` if the animation was aborted,
1330
+ * `false` if it was not aborted.
1331
+ */
1332
+ async animateOpen() {
1333
+ const surfaceEl = this.surfaceEl;
1334
+ const slotEl = this.slotEl;
1335
+ if (!surfaceEl || !slotEl) return true;
1336
+ const openDirection = this.openDirection;
1337
+ this.dispatchEvent(new Event('opening'));
1338
+ // needs to be imperative because we don't want to mix animation and Lit
1339
+ // render timing
1340
+ surfaceEl.classList.toggle('animating', true);
1341
+ const signal = this.openCloseAnimationSignal.start();
1342
+ const height = surfaceEl.offsetHeight;
1343
+ const openingUpwards = openDirection === 'UP';
1344
+ const children = this.items;
1345
+ const FULL_DURATION = 500;
1346
+ const SURFACE_OPACITY_DURATION = 50;
1347
+ const ITEM_OPACITY_DURATION = 250;
1348
+ // We want to fit every child fade-in animation within the full duration of
1349
+ // the animation.
1350
+ const DELAY_BETWEEN_ITEMS = (FULL_DURATION - ITEM_OPACITY_DURATION) / children.length;
1351
+ const surfaceHeightAnimation = surfaceEl.animate([{
1352
+ height: '0px'
1353
+ }, {
1354
+ height: `${height}px`
1355
+ }], {
1356
+ duration: FULL_DURATION,
1357
+ easing: EASING.EMPHASIZED
1358
+ });
1359
+ // When we are opening upwards, we want to make sure the last item is always
1360
+ // in view, so we need to translate it upwards the opposite direction of the
1361
+ // height animation
1362
+ const upPositionCorrectionAnimation = slotEl.animate([{
1363
+ transform: openingUpwards ? `translateY(-${height}px)` : ''
1364
+ }, {
1365
+ transform: ''
1366
+ }], {
1367
+ duration: FULL_DURATION,
1368
+ easing: EASING.EMPHASIZED
1369
+ });
1370
+ const surfaceOpacityAnimation = surfaceEl.animate([{
1371
+ opacity: 0
1372
+ }, {
1373
+ opacity: 1
1374
+ }], SURFACE_OPACITY_DURATION);
1375
+ const childrenAnimations = [];
1376
+ for (let i = 0; i < children.length; i++) {
1377
+ // If we are animating upwards, then reverse the children list.
1378
+ const directionalIndex = openingUpwards ? children.length - 1 - i : i;
1379
+ const child = children[directionalIndex];
1380
+ const animation = child.animate([{
1381
+ opacity: 0
1382
+ }, {
1383
+ opacity: 1
1384
+ }], {
1385
+ duration: ITEM_OPACITY_DURATION,
1386
+ delay: DELAY_BETWEEN_ITEMS * i
1387
+ });
1388
+ // Make them all initially hidden and then clean up at the end of each
1389
+ // animation.
1390
+ child.classList.toggle('md-menu-hidden', true);
1391
+ animation.addEventListener('finish', () => {
1392
+ child.classList.toggle('md-menu-hidden', false);
1393
+ });
1394
+ childrenAnimations.push([child, animation]);
1395
+ }
1396
+ let resolveAnimation = value => {};
1397
+ const animationFinished = new Promise(resolve => {
1398
+ resolveAnimation = resolve;
1399
+ });
1400
+ signal.addEventListener('abort', () => {
1401
+ surfaceHeightAnimation.cancel();
1402
+ upPositionCorrectionAnimation.cancel();
1403
+ surfaceOpacityAnimation.cancel();
1404
+ childrenAnimations.forEach(([child, animation]) => {
1405
+ child.classList.toggle('md-menu-hidden', false);
1406
+ animation.cancel();
1407
+ });
1408
+ resolveAnimation(true);
1409
+ });
1410
+ surfaceHeightAnimation.addEventListener('finish', () => {
1411
+ surfaceEl.classList.toggle('animating', false);
1412
+ this.openCloseAnimationSignal.finish();
1413
+ resolveAnimation(false);
1414
+ });
1415
+ return await animationFinished;
1416
+ }
1417
+ /**
1418
+ * Performs the closing animation:
1419
+ *
1420
+ * https://direct.googleplex.com/#/spec/295000003+271060003
1421
+ */
1422
+ animateClose() {
1423
+ let resolve;
1424
+ // This promise blocks the surface position controller from setting
1425
+ // display: none on the surface which will interfere with this animation.
1426
+ const animationEnded = new Promise(res => {
1427
+ resolve = res;
1428
+ });
1429
+ const surfaceEl = this.surfaceEl;
1430
+ const slotEl = this.slotEl;
1431
+ if (!surfaceEl || !slotEl) {
1432
+ resolve(false);
1433
+ return animationEnded;
1434
+ }
1435
+ const openDirection = this.openDirection;
1436
+ const closingDownwards = openDirection === 'UP';
1437
+ this.dispatchEvent(new Event('closing'));
1438
+ // needs to be imperative because we don't want to mix animation and Lit
1439
+ // render timing
1440
+ surfaceEl.classList.toggle('animating', true);
1441
+ const signal = this.openCloseAnimationSignal.start();
1442
+ const height = surfaceEl.offsetHeight;
1443
+ const children = this.items;
1444
+ const FULL_DURATION = 150;
1445
+ const SURFACE_OPACITY_DURATION = 50;
1446
+ // The surface fades away at the very end
1447
+ const SURFACE_OPACITY_DELAY = FULL_DURATION - SURFACE_OPACITY_DURATION;
1448
+ const ITEM_OPACITY_DURATION = 50;
1449
+ const ITEM_OPACITY_INITIAL_DELAY = 50;
1450
+ const END_HEIGHT_PERCENTAGE = 0.35;
1451
+ // We want to fit every child fade-out animation within the full duration of
1452
+ // the animation.
1453
+ const DELAY_BETWEEN_ITEMS = (FULL_DURATION - ITEM_OPACITY_INITIAL_DELAY - ITEM_OPACITY_DURATION) / children.length;
1454
+ // The mock has the animation shrink to 35%
1455
+ const surfaceHeightAnimation = surfaceEl.animate([{
1456
+ height: `${height}px`
1457
+ }, {
1458
+ height: `${height * END_HEIGHT_PERCENTAGE}px`
1459
+ }], {
1460
+ duration: FULL_DURATION,
1461
+ easing: EASING.EMPHASIZED_ACCELERATE
1462
+ });
1463
+ // When we are closing downwards, we want to make sure the last item is
1464
+ // always in view, so we need to translate it upwards the opposite direction
1465
+ // of the height animation
1466
+ const downPositionCorrectionAnimation = slotEl.animate([{
1467
+ transform: ''
1468
+ }, {
1469
+ transform: closingDownwards ? `translateY(-${height * (1 - END_HEIGHT_PERCENTAGE)}px)` : ''
1470
+ }], {
1471
+ duration: FULL_DURATION,
1472
+ easing: EASING.EMPHASIZED_ACCELERATE
1473
+ });
1474
+ const surfaceOpacityAnimation = surfaceEl.animate([{
1475
+ opacity: 1
1476
+ }, {
1477
+ opacity: 0
1478
+ }], {
1479
+ duration: SURFACE_OPACITY_DURATION,
1480
+ delay: SURFACE_OPACITY_DELAY
1481
+ });
1482
+ const childrenAnimations = [];
1483
+ for (let i = 0; i < children.length; i++) {
1484
+ // If the animation is closing upwards, then reverse the list of
1485
+ // children so that we animate in the opposite direction.
1486
+ const directionalIndex = closingDownwards ? i : children.length - 1 - i;
1487
+ const child = children[directionalIndex];
1488
+ const animation = child.animate([{
1489
+ opacity: 1
1490
+ }, {
1491
+ opacity: 0
1492
+ }], {
1493
+ duration: ITEM_OPACITY_DURATION,
1494
+ delay: ITEM_OPACITY_INITIAL_DELAY + DELAY_BETWEEN_ITEMS * i
1495
+ });
1496
+ // Make sure the items stay hidden at the end of each child animation.
1497
+ // We clean this up at the end of the overall animation.
1498
+ animation.addEventListener('finish', () => {
1499
+ child.classList.toggle('md-menu-hidden', true);
1500
+ });
1501
+ childrenAnimations.push([child, animation]);
1502
+ }
1503
+ signal.addEventListener('abort', () => {
1504
+ surfaceHeightAnimation.cancel();
1505
+ downPositionCorrectionAnimation.cancel();
1506
+ surfaceOpacityAnimation.cancel();
1507
+ childrenAnimations.forEach(([child, animation]) => {
1508
+ animation.cancel();
1509
+ child.classList.toggle('md-menu-hidden', false);
1510
+ });
1511
+ resolve(false);
1512
+ });
1513
+ surfaceHeightAnimation.addEventListener('finish', () => {
1514
+ surfaceEl.classList.toggle('animating', false);
1515
+ childrenAnimations.forEach(([child]) => {
1516
+ child.classList.toggle('md-menu-hidden', false);
1517
+ });
1518
+ this.openCloseAnimationSignal.finish();
1519
+ this.dispatchEvent(new Event('closed'));
1520
+ resolve(true);
1521
+ });
1522
+ return animationEnded;
1523
+ }
1524
+ handleKeydown(event) {
1525
+ // At any key event, the pointer interaction is done so we need to clear our
1526
+ // cached pointerpath. This handles the case where the user clicks on the
1527
+ // anchor, and then hits shift+tab
1528
+ this.pointerPath = [];
1529
+ this.listController.handleKeydown(event);
1530
+ }
1531
+ setUpGlobalEventListeners() {
1532
+ document.addEventListener('click', this.onDocumentClick, {
1533
+ capture: true
1534
+ });
1535
+ window.addEventListener('pointerdown', this.onWindowPointerdown);
1536
+ document.addEventListener('resize', this.onWindowResize, {
1537
+ passive: true
1538
+ });
1539
+ window.addEventListener('resize', this.onWindowResize, {
1540
+ passive: true
1541
+ });
1542
+ }
1543
+ cleanUpGlobalEventListeners() {
1544
+ document.removeEventListener('click', this.onDocumentClick, {
1545
+ capture: true
1546
+ });
1547
+ window.removeEventListener('pointerdown', this.onWindowPointerdown);
1548
+ document.removeEventListener('resize', this.onWindowResize);
1549
+ window.removeEventListener('resize', this.onWindowResize);
1550
+ }
1551
+ onCloseMenu() {
1552
+ this.close();
1553
+ }
1554
+ onDeactivateItems(event) {
1555
+ event.stopPropagation();
1556
+ this.listController.onDeactivateItems();
1557
+ }
1558
+ onRequestActivation(event) {
1559
+ event.stopPropagation();
1560
+ this.listController.onRequestActivation(event);
1561
+ }
1562
+ handleDeactivateTypeahead(event) {
1563
+ // stopPropagation so that this does not deactivate any typeaheads in menus
1564
+ // nested above it e.g. md-sub-menu
1565
+ event.stopPropagation();
1566
+ this.typeaheadActive = false;
1567
+ }
1568
+ handleActivateTypeahead(event) {
1569
+ // stopPropagation so that this does not activate any typeaheads in menus
1570
+ // nested above it e.g. md-sub-menu
1571
+ event.stopPropagation();
1572
+ this.typeaheadActive = true;
1573
+ }
1574
+ handleStayOpenOnFocusout(event) {
1575
+ event.stopPropagation();
1576
+ this.stayOpenOnFocusout = true;
1577
+ }
1578
+ handleCloseOnFocusout(event) {
1579
+ event.stopPropagation();
1580
+ this.stayOpenOnFocusout = false;
1581
+ }
1582
+ close() {
1583
+ this.open = false;
1584
+ const maybeSubmenu = this.slotItems;
1585
+ maybeSubmenu.forEach(item => {
1586
+ var _item$close;
1587
+ (_item$close = item.close) === null || _item$close === void 0 || _item$close.call(item);
1588
+ });
1589
+ }
1590
+ show() {
1591
+ this.open = true;
1592
+ }
1593
+ /**
1594
+ * Activates the next item in the menu. If at the end of the menu, the first
1595
+ * item will be activated.
1596
+ *
1597
+ * @return The activated menu item or `null` if there are no items.
1598
+ */
1599
+ activateNextItem() {
1600
+ return this.listController.activateNextItem() ?? null;
1601
+ }
1602
+ /**
1603
+ * Activates the previous item in the menu. If at the start of the menu, the
1604
+ * last item will be activated.
1605
+ *
1606
+ * @return The activated menu item or `null` if there are no items.
1607
+ */
1608
+ activatePreviousItem() {
1609
+ return this.listController.activatePreviousItem() ?? null;
1610
+ }
1611
+ /**
1612
+ * Repositions the menu if it is open.
1613
+ *
1614
+ * Useful for the case where document or window-positioned menus have their
1615
+ * anchors moved while open.
1616
+ */
1617
+ reposition() {
1618
+ if (this.open) {
1619
+ this.menuPositionController.position();
1620
+ }
1621
+ }
1622
+ }
1623
+ __decorate([e$1('.menu')], Menu.prototype, "surfaceEl", void 0);
1624
+ __decorate([e$1('slot')], Menu.prototype, "slotEl", void 0);
1625
+ __decorate([n$1()], Menu.prototype, "anchor", void 0);
1626
+ __decorate([n$1()], Menu.prototype, "positioning", void 0);
1627
+ __decorate([n$1({
1628
+ type: Boolean
1629
+ })], Menu.prototype, "quick", void 0);
1630
+ __decorate([n$1({
1631
+ type: Boolean,
1632
+ attribute: 'has-overflow'
1633
+ })], Menu.prototype, "hasOverflow", void 0);
1634
+ __decorate([n$1({
1635
+ type: Boolean,
1636
+ reflect: true
1637
+ })], Menu.prototype, "open", void 0);
1638
+ __decorate([n$1({
1639
+ type: Number,
1640
+ attribute: 'x-offset'
1641
+ })], Menu.prototype, "xOffset", void 0);
1642
+ __decorate([n$1({
1643
+ type: Number,
1644
+ attribute: 'y-offset'
1645
+ })], Menu.prototype, "yOffset", void 0);
1646
+ __decorate([n$1({
1647
+ type: Boolean,
1648
+ attribute: 'no-horizontal-flip'
1649
+ })], Menu.prototype, "noHorizontalFlip", void 0);
1650
+ __decorate([n$1({
1651
+ type: Boolean,
1652
+ attribute: 'no-vertical-flip'
1653
+ })], Menu.prototype, "noVerticalFlip", void 0);
1654
+ __decorate([n$1({
1655
+ type: Number,
1656
+ attribute: 'typeahead-delay'
1657
+ })], Menu.prototype, "typeaheadDelay", void 0);
1658
+ __decorate([n$1({
1659
+ attribute: 'anchor-corner'
1660
+ })], Menu.prototype, "anchorCorner", void 0);
1661
+ __decorate([n$1({
1662
+ attribute: 'menu-corner'
1663
+ })], Menu.prototype, "menuCorner", void 0);
1664
+ __decorate([n$1({
1665
+ type: Boolean,
1666
+ attribute: 'stay-open-on-outside-click'
1667
+ })], Menu.prototype, "stayOpenOnOutsideClick", void 0);
1668
+ __decorate([n$1({
1669
+ type: Boolean,
1670
+ attribute: 'stay-open-on-focusout'
1671
+ })], Menu.prototype, "stayOpenOnFocusout", void 0);
1672
+ __decorate([n$1({
1673
+ type: Boolean,
1674
+ attribute: 'skip-restore-focus'
1675
+ })], Menu.prototype, "skipRestoreFocus", void 0);
1676
+ __decorate([n$1({
1677
+ attribute: 'default-focus'
1678
+ })], Menu.prototype, "defaultFocus", void 0);
1679
+ __decorate([n$1({
1680
+ type: Boolean,
1681
+ attribute: 'no-navigation-wrap'
1682
+ })], Menu.prototype, "noNavigationWrap", void 0);
1683
+ __decorate([o({
1684
+ flatten: true
1685
+ })], Menu.prototype, "slotItems", void 0);
1686
+ __decorate([r()], Menu.prototype, "typeaheadActive", void 0);
1687
+
1688
+ /**
1689
+ * @license
1690
+ * Copyright 2024 Google LLC
1691
+ * SPDX-License-Identifier: Apache-2.0
1692
+ */
1693
+ // Generated stylesheet for ./menu/internal/menu-styles.css.
1694
+ 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}}
1695
+ `;
1696
+
1697
+ /**
1698
+ * @license
1699
+ * Copyright 2022 Google LLC
1700
+ * SPDX-License-Identifier: Apache-2.0
1701
+ */
1702
+ /**
1703
+ * @summary Menus display a list of choices on a temporary surface.
1704
+ *
1705
+ * @description
1706
+ * Menus appear when users interact with a button, action, or other control.
1707
+ *
1708
+ * They can be opened from a variety of elements, most commonly icon buttons,
1709
+ * buttons, and text fields.
1710
+ *
1711
+ * md-menu listens for the `close-menu` and `deselect-items` events.
1712
+ *
1713
+ * - `close-menu` closes the menu when dispatched from a child element.
1714
+ * - `deselect-items` deselects all of its immediate menu-item children.
1715
+ *
1716
+ * @example
1717
+ * ```html
1718
+ * <div style="position:relative;">
1719
+ * <button
1720
+ * id="anchor"
1721
+ * @click=${() => this.menuRef.value.show()}>
1722
+ * Click to open menu
1723
+ * </button>
1724
+ * <!--
1725
+ * `has-overflow` is required when using a submenu which overflows the
1726
+ * menu's contents.
1727
+ *
1728
+ * Additionally, `anchor` ingests an idref which do not pass through shadow
1729
+ * roots. You can also set `.anchorElement` to an element reference if
1730
+ * necessary.
1731
+ * -->
1732
+ * <md-menu anchor="anchor" has-overflow ${ref(menuRef)}>
1733
+ * <md-menu-item headline="This is a headline"></md-menu-item>
1734
+ * <md-sub-menu>
1735
+ * <md-menu-item
1736
+ * slot="item"
1737
+ * headline="this is a submenu item">
1738
+ * </md-menu-item>
1739
+ * <md-menu slot="menu">
1740
+ * <md-menu-item headline="This is an item inside a submenu">
1741
+ * </md-menu-item>
1742
+ * </md-menu>
1743
+ * </md-sub-menu>
1744
+ * </md-menu>
1745
+ * </div>
1746
+ * ```
1747
+ *
1748
+ * @final
1749
+ * @suppress {visibility}
1750
+ */
1751
+ let MdMenu = class MdMenu extends Menu {};
1752
+ MdMenu.styles = [styles$3];
1753
+ MdMenu = __decorate([t('md-menu')], MdMenu);
1754
+
1755
+ /**
1756
+ * @license
1757
+ * Copyright 2023 Google LLC
1758
+ * SPDX-License-Identifier: Apache-2.0
1759
+ */
1760
+ /**
1761
+ * A validator that provides constraint validation that emulates `<select>`
1762
+ * validation.
1763
+ */
1764
+ class SelectValidator extends Validator {
1765
+ computeValidity(state) {
1766
+ if (!this.selectControl) {
1767
+ // Lazily create the platform select
1768
+ this.selectControl = document.createElement('select');
1769
+ }
1770
+ D(b`<option value=${state.value}></option>`, this.selectControl);
1771
+ this.selectControl.value = state.value;
1772
+ this.selectControl.required = state.required;
1773
+ return {
1774
+ validity: this.selectControl.validity,
1775
+ validationMessage: this.selectControl.validationMessage
1776
+ };
1777
+ }
1778
+ equals(prev, next) {
1779
+ return prev.value === next.value && prev.required === next.required;
1780
+ }
1781
+ copy({
1782
+ value,
1783
+ required
1784
+ }) {
1785
+ return {
1786
+ value,
1787
+ required
1788
+ };
1789
+ }
1790
+ }
1791
+
1792
+ /**
1793
+ * @license
1794
+ * Copyright 2023 Google LLC
1795
+ * SPDX-License-Identifier: Apache-2.0
1796
+ */
1797
+ /**
1798
+ * Given a list of select options, this function will return an array of
1799
+ * SelectOptionRecords that are selected.
1800
+ *
1801
+ * @return An array of SelectOptionRecords describing the options that are
1802
+ * selected.
1803
+ */
1804
+ function getSelectedItems(items) {
1805
+ const selectedItemRecords = [];
1806
+ for (let i = 0; i < items.length; i++) {
1807
+ const item = items[i];
1808
+ if (item.selected) {
1809
+ selectedItemRecords.push([item, i]);
1810
+ }
1811
+ }
1812
+ return selectedItemRecords;
1813
+ }
1814
+
1815
+ /**
1816
+ * @license
1817
+ * Copyright 2023 Google LLC
1818
+ * SPDX-License-Identifier: Apache-2.0
1819
+ */
1820
+ var _a;
1821
+ const VALUE = Symbol('value');
1822
+ // Separate variable needed for closure.
1823
+ const selectBaseClass = mixinDelegatesAria(mixinOnReportValidity(mixinConstraintValidation(mixinFormAssociated(mixinElementInternals(i)))));
1824
+ /**
1825
+ * @fires change {Event} The native `change` event on
1826
+ * [`<input>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event)
1827
+ * --bubbles
1828
+ * @fires input {InputEvent} The native `input` event on
1829
+ * [`<input>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event)
1830
+ * --bubbles --composed
1831
+ * @fires opening {Event} Fired when the select's menu is about to open.
1832
+ * @fires opened {Event} Fired when the select's menu has finished animations
1833
+ * and opened.
1834
+ * @fires closing {Event} Fired when the select's menu is about to close.
1835
+ * @fires closed {Event} Fired when the select's menu has finished animations
1836
+ * and closed.
1837
+ */
1838
+ class Select extends selectBaseClass {
1839
+ /**
1840
+ * The value of the currently selected option.
1841
+ *
1842
+ * Note: For SSR, set `[selected]` on the requested option and `displayText`
1843
+ * rather than setting `value` setting `value` will incur a DOM query.
1844
+ */
1845
+ get value() {
1846
+ return this[VALUE];
1847
+ }
1848
+ set value(value) {
1849
+ this.lastUserSetValue = value;
1850
+ this.select(value);
1851
+ }
1852
+ get options() {
1853
+ var _this$menu;
1854
+ // NOTE: this does a DOM query.
1855
+ return ((_this$menu = this.menu) === null || _this$menu === void 0 ? void 0 : _this$menu.items) ?? [];
1856
+ }
1857
+ /**
1858
+ * The index of the currently selected option.
1859
+ *
1860
+ * Note: For SSR, set `[selected]` on the requested option and `displayText`
1861
+ * rather than setting `selectedIndex` setting `selectedIndex` will incur a
1862
+ * DOM query.
1863
+ */
1864
+ get selectedIndex() {
1865
+ // tslint:disable-next-line:enforce-name-casing
1866
+ const [_option, index] = (this.getSelectedOptions() ?? [])[0] ?? [];
1867
+ return index ?? -1;
1868
+ }
1869
+ set selectedIndex(index) {
1870
+ this.lastUserSetSelectedIndex = index;
1871
+ this.selectIndex(index);
1872
+ }
1873
+ /**
1874
+ * Returns an array of selected options.
1875
+ *
1876
+ * NOTE: md-select only supports single selection.
1877
+ */
1878
+ get selectedOptions() {
1879
+ return (this.getSelectedOptions() ?? []).map(([option]) => option);
1880
+ }
1881
+ get hasError() {
1882
+ return this.error || this.nativeError;
1883
+ }
1884
+ constructor() {
1885
+ super();
1886
+ /**
1887
+ * Opens the menu synchronously with no animation.
1888
+ */
1889
+ this.quick = false;
1890
+ /**
1891
+ * Whether or not the select is required.
1892
+ */
1893
+ this.required = false;
1894
+ /**
1895
+ * The error message that replaces supporting text when `error` is true. If
1896
+ * `errorText` is an empty string, then the supporting text will continue to
1897
+ * show.
1898
+ *
1899
+ * This error message overrides the error message displayed by
1900
+ * `reportValidity()`.
1901
+ */
1902
+ this.errorText = '';
1903
+ /**
1904
+ * The floating label for the field.
1905
+ */
1906
+ this.label = '';
1907
+ /**
1908
+ * Disables the asterisk on the floating label, when the select is
1909
+ * required.
1910
+ */
1911
+ this.noAsterisk = false;
1912
+ /**
1913
+ * Conveys additional information below the select, such as how it should
1914
+ * be used.
1915
+ */
1916
+ this.supportingText = '';
1917
+ /**
1918
+ * Gets or sets whether or not the select is in a visually invalid state.
1919
+ *
1920
+ * This error state overrides the error state controlled by
1921
+ * `reportValidity()`.
1922
+ */
1923
+ this.error = false;
1924
+ /**
1925
+ * Whether or not the underlying md-menu should be position: fixed to display
1926
+ * in a top-level manner, or position: absolute.
1927
+ *
1928
+ * position:fixed is useful for cases where select is inside of another
1929
+ * element with stacking context and hidden overflows such as `md-dialog`.
1930
+ */
1931
+ this.menuPositioning = 'popover';
1932
+ /**
1933
+ * Clamps the menu-width to the width of the select.
1934
+ */
1935
+ this.clampMenuWidth = false;
1936
+ /**
1937
+ * The max time between the keystrokes of the typeahead select / menu behavior
1938
+ * before it clears the typeahead buffer.
1939
+ */
1940
+ this.typeaheadDelay = DEFAULT_TYPEAHEAD_BUFFER_TIME;
1941
+ /**
1942
+ * Whether or not the text field has a leading icon. Used for SSR.
1943
+ */
1944
+ this.hasLeadingIcon = false;
1945
+ /**
1946
+ * Text to display in the field. Only set for SSR.
1947
+ */
1948
+ this.displayText = '';
1949
+ /**
1950
+ * Whether the menu should be aligned to the start or the end of the select's
1951
+ * textbox.
1952
+ */
1953
+ this.menuAlign = 'start';
1954
+ this[_a] = '';
1955
+ /**
1956
+ * Used for initializing select when the user sets the `value` directly.
1957
+ */
1958
+ this.lastUserSetValue = null;
1959
+ /**
1960
+ * Used for initializing select when the user sets the `selectedIndex`
1961
+ * directly.
1962
+ */
1963
+ this.lastUserSetSelectedIndex = null;
1964
+ /**
1965
+ * Used for `input` and `change` event change detection.
1966
+ */
1967
+ this.lastSelectedOption = null;
1968
+ // tslint:disable-next-line:enforce-name-casing
1969
+ this.lastSelectedOptionRecords = [];
1970
+ /**
1971
+ * Whether or not a native error has been reported via `reportValidity()`.
1972
+ */
1973
+ this.nativeError = false;
1974
+ /**
1975
+ * The validation message displayed from a native error via
1976
+ * `reportValidity()`.
1977
+ */
1978
+ this.nativeErrorText = '';
1979
+ this.focused = false;
1980
+ this.open = false;
1981
+ this.defaultFocus = FocusState.NONE;
1982
+ // Have to keep track of previous open because it's state and private and thus
1983
+ // cannot be tracked in PropertyValues<this> map.
1984
+ this.prevOpen = this.open;
1985
+ this.selectWidth = 0;
1986
+ this.addEventListener('focus', this.handleFocus.bind(this));
1987
+ this.addEventListener('blur', this.handleBlur.bind(this));
1988
+ }
1989
+ /**
1990
+ * Selects an option given the value of the option, and updates MdSelect's
1991
+ * value.
1992
+ */
1993
+ select(value) {
1994
+ const optionToSelect = this.options.find(option => option.value === value);
1995
+ if (optionToSelect) {
1996
+ this.selectItem(optionToSelect);
1997
+ }
1998
+ }
1999
+ /**
2000
+ * Selects an option given the index of the option, and updates MdSelect's
2001
+ * value.
2002
+ */
2003
+ selectIndex(index) {
2004
+ const optionToSelect = this.options[index];
2005
+ if (optionToSelect) {
2006
+ this.selectItem(optionToSelect);
2007
+ }
2008
+ }
2009
+ /**
2010
+ * Reset the select to its default value.
2011
+ */
2012
+ reset() {
2013
+ for (const option of this.options) {
2014
+ option.selected = option.hasAttribute('selected');
2015
+ }
2016
+ this.updateValueAndDisplayText();
2017
+ this.nativeError = false;
2018
+ this.nativeErrorText = '';
2019
+ }
2020
+ /** Shows the picker. If it's already open, this is a no-op. */
2021
+ showPicker() {
2022
+ this.open = true;
2023
+ }
2024
+ [(_a = VALUE, onReportValidity)](invalidEvent) {
2025
+ // Prevent default pop-up behavior.
2026
+ invalidEvent === null || invalidEvent === void 0 || invalidEvent.preventDefault();
2027
+ const prevMessage = this.getErrorText();
2028
+ this.nativeError = !!invalidEvent;
2029
+ this.nativeErrorText = this.validationMessage;
2030
+ if (prevMessage === this.getErrorText()) {
2031
+ var _this$field;
2032
+ (_this$field = this.field) === null || _this$field === void 0 || _this$field.reannounceError();
2033
+ }
2034
+ }
2035
+ update(changed) {
2036
+ // In SSR the options will be ready to query, so try to figure out what
2037
+ // the value and display text should be.
2038
+ if (!this.hasUpdated) {
2039
+ this.initUserSelection();
2040
+ }
2041
+ // We have just opened the menu.
2042
+ // We are only able to check for the select's rect in `update()` instead of
2043
+ // having to wait for `updated()` because the menu can never be open on
2044
+ // first render since it is not settable and Lit SSR does not support click
2045
+ // events which would open the menu.
2046
+ if (this.prevOpen !== this.open && this.open) {
2047
+ const selectRect = this.getBoundingClientRect();
2048
+ this.selectWidth = selectRect.width;
2049
+ }
2050
+ this.prevOpen = this.open;
2051
+ super.update(changed);
2052
+ }
2053
+ render() {
2054
+ return b`
2055
+ <span
2056
+ class="select ${e$2(this.getRenderClasses())}"
2057
+ @focusout=${this.handleFocusout}>
2058
+ ${this.renderField()} ${this.renderMenu()}
2059
+ </span>
2060
+ `;
2061
+ }
2062
+ async firstUpdated(changed) {
2063
+ var _this$menu2;
2064
+ await ((_this$menu2 = this.menu) === null || _this$menu2 === void 0 ? void 0 : _this$menu2.updateComplete);
2065
+ // If this has been handled on update already due to SSR, try again.
2066
+ if (!this.lastSelectedOptionRecords.length) {
2067
+ this.initUserSelection();
2068
+ }
2069
+ // Case for when the DOM is streaming, there are no children, and a child
2070
+ // has [selected] set on it, we need to wait for DOM to render something.
2071
+ if (!this.lastSelectedOptionRecords.length && true && !this.options.length) {
2072
+ setTimeout(() => {
2073
+ this.updateValueAndDisplayText();
2074
+ });
2075
+ }
2076
+ super.firstUpdated(changed);
2077
+ }
2078
+ getRenderClasses() {
2079
+ return {
2080
+ 'disabled': this.disabled,
2081
+ 'error': this.error,
2082
+ 'open': this.open
2083
+ };
2084
+ }
2085
+ renderField() {
2086
+ const ariaLabel = this.ariaLabel || this.label;
2087
+ return u`
2088
+ <${this.fieldTag}
2089
+ aria-haspopup="listbox"
2090
+ role="combobox"
2091
+ part="field"
2092
+ id="field"
2093
+ tabindex=${this.disabled ? '-1' : '0'}
2094
+ aria-label=${ariaLabel || A}
2095
+ aria-describedby="description"
2096
+ aria-expanded=${this.open ? 'true' : 'false'}
2097
+ aria-controls="listbox"
2098
+ class="field"
2099
+ label=${this.label}
2100
+ ?no-asterisk=${this.noAsterisk}
2101
+ .focused=${this.focused || this.open}
2102
+ .populated=${!!this.displayText}
2103
+ .disabled=${this.disabled}
2104
+ .required=${this.required}
2105
+ .error=${this.hasError}
2106
+ ?has-start=${this.hasLeadingIcon}
2107
+ has-end
2108
+ supporting-text=${this.supportingText}
2109
+ error-text=${this.getErrorText()}
2110
+ @keydown=${this.handleKeydown}
2111
+ @click=${this.handleClick}>
2112
+ ${this.renderFieldContent()}
2113
+ <div id="description" slot="aria-describedby"></div>
2114
+ </${this.fieldTag}>`;
2115
+ }
2116
+ renderFieldContent() {
2117
+ return [this.renderLeadingIcon(), this.renderLabel(), this.renderTrailingIcon()];
2118
+ }
2119
+ renderLeadingIcon() {
2120
+ return b`
2121
+ <span class="icon leading" slot="start">
2122
+ <slot name="leading-icon" @slotchange=${this.handleIconChange}></slot>
2123
+ </span>
2124
+ `;
2125
+ }
2126
+ renderTrailingIcon() {
2127
+ return b`
2128
+ <span class="icon trailing" slot="end">
2129
+ <slot name="trailing-icon" @slotchange=${this.handleIconChange}>
2130
+ <svg height="5" viewBox="7 10 10 5" focusable="false">
2131
+ <polygon
2132
+ class="down"
2133
+ stroke="none"
2134
+ fill-rule="evenodd"
2135
+ points="7 10 12 15 17 10"></polygon>
2136
+ <polygon
2137
+ class="up"
2138
+ stroke="none"
2139
+ fill-rule="evenodd"
2140
+ points="7 15 12 10 17 15"></polygon>
2141
+ </svg>
2142
+ </slot>
2143
+ </span>
2144
+ `;
2145
+ }
2146
+ renderLabel() {
2147
+ // need to render &nbsp; so that line-height can apply and give it a
2148
+ // non-zero height
2149
+ return b`<div id="label">${this.displayText || b`&nbsp;`}</div>`;
2150
+ }
2151
+ renderMenu() {
2152
+ const ariaLabel = this.label || this.ariaLabel;
2153
+ return b`<div class="menu-wrapper">
2154
+ <md-menu
2155
+ id="listbox"
2156
+ .defaultFocus=${this.defaultFocus}
2157
+ role="listbox"
2158
+ tabindex="-1"
2159
+ aria-label=${ariaLabel || A}
2160
+ stay-open-on-focusout
2161
+ part="menu"
2162
+ exportparts="focus-ring: menu-focus-ring"
2163
+ anchor="field"
2164
+ style=${o$1({
2165
+ '--__menu-min-width': `${this.selectWidth}px`,
2166
+ '--__menu-max-width': this.clampMenuWidth ? `${this.selectWidth}px` : undefined
2167
+ })}
2168
+ no-navigation-wrap
2169
+ .open=${this.open}
2170
+ .quick=${this.quick}
2171
+ .positioning=${this.menuPositioning}
2172
+ .typeaheadDelay=${this.typeaheadDelay}
2173
+ .anchorCorner=${this.menuAlign === 'start' ? 'end-start' : 'end-end'}
2174
+ .menuCorner=${this.menuAlign === 'start' ? 'start-start' : 'start-end'}
2175
+ @opening=${this.handleOpening}
2176
+ @opened=${this.redispatchEvent}
2177
+ @closing=${this.redispatchEvent}
2178
+ @closed=${this.handleClosed}
2179
+ @close-menu=${this.handleCloseMenu}
2180
+ @request-selection=${this.handleRequestSelection}
2181
+ @request-deselection=${this.handleRequestDeselection}>
2182
+ ${this.renderMenuContent()}
2183
+ </md-menu>
2184
+ </div>`;
2185
+ }
2186
+ renderMenuContent() {
2187
+ return b`<slot></slot>`;
2188
+ }
2189
+ /**
2190
+ * Handles opening the select on keydown and typahead selection when the menu
2191
+ * is closed.
2192
+ */
2193
+ handleKeydown(event) {
2194
+ if (this.open || this.disabled || !this.menu) {
2195
+ return;
2196
+ }
2197
+ const typeaheadController = this.menu.typeaheadController;
2198
+ const isOpenKey = event.code === 'Space' || event.code === 'ArrowDown' || event.code === 'ArrowUp' || event.code === 'End' || event.code === 'Home' || event.code === 'Enter';
2199
+ // Do not open if currently typing ahead because the user may be typing the
2200
+ // spacebar to match a word with a space
2201
+ if (!typeaheadController.isTypingAhead && isOpenKey) {
2202
+ event.preventDefault();
2203
+ this.open = true;
2204
+ // https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/#kbd_label
2205
+ switch (event.code) {
2206
+ case 'Space':
2207
+ case 'ArrowDown':
2208
+ case 'Enter':
2209
+ // We will handle focusing last selected item in this.handleOpening()
2210
+ this.defaultFocus = FocusState.NONE;
2211
+ break;
2212
+ case 'End':
2213
+ this.defaultFocus = FocusState.LAST_ITEM;
2214
+ break;
2215
+ case 'ArrowUp':
2216
+ case 'Home':
2217
+ this.defaultFocus = FocusState.FIRST_ITEM;
2218
+ break;
2219
+ }
2220
+ return;
2221
+ }
2222
+ const isPrintableKey = event.key.length === 1;
2223
+ // Handles typing ahead when the menu is closed by delegating the event to
2224
+ // the underlying menu's typeaheadController
2225
+ if (isPrintableKey) {
2226
+ var _this$labelEl, _this$labelEl$setAttr;
2227
+ typeaheadController.onKeydown(event);
2228
+ event.preventDefault();
2229
+ const {
2230
+ lastActiveRecord
2231
+ } = typeaheadController;
2232
+ if (!lastActiveRecord) {
2233
+ return;
2234
+ }
2235
+ (_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');
2236
+ const hasChanged = this.selectItem(lastActiveRecord[TYPEAHEAD_RECORD.ITEM]);
2237
+ if (hasChanged) {
2238
+ this.dispatchInteractionEvents();
2239
+ }
2240
+ }
2241
+ }
2242
+ handleClick() {
2243
+ this.open = !this.open;
2244
+ }
2245
+ handleFocus() {
2246
+ this.focused = true;
2247
+ }
2248
+ handleBlur() {
2249
+ this.focused = false;
2250
+ }
2251
+ /**
2252
+ * Handles closing the menu when the focus leaves the select's subtree.
2253
+ */
2254
+ handleFocusout(event) {
2255
+ // Don't close the menu if we are switching focus between menu,
2256
+ // select-option, and field
2257
+ if (event.relatedTarget && isElementInSubtree(event.relatedTarget, this)) {
2258
+ return;
2259
+ }
2260
+ this.open = false;
2261
+ }
2262
+ /**
2263
+ * Gets a list of all selected select options as a list item record array.
2264
+ *
2265
+ * @return An array of selected list option records.
2266
+ */
2267
+ getSelectedOptions() {
2268
+ if (!this.menu) {
2269
+ this.lastSelectedOptionRecords = [];
2270
+ return null;
2271
+ }
2272
+ const items = this.menu.items;
2273
+ this.lastSelectedOptionRecords = getSelectedItems(items);
2274
+ return this.lastSelectedOptionRecords;
2275
+ }
2276
+ async getUpdateComplete() {
2277
+ var _this$menu3;
2278
+ await ((_this$menu3 = this.menu) === null || _this$menu3 === void 0 ? void 0 : _this$menu3.updateComplete);
2279
+ return super.getUpdateComplete();
2280
+ }
2281
+ /**
2282
+ * Gets the selected options from the DOM, and updates the value and display
2283
+ * text to the first selected option's value and headline respectively.
2284
+ *
2285
+ * @return Whether or not the selected option has changed since last update.
2286
+ */
2287
+ updateValueAndDisplayText() {
2288
+ const selectedOptions = this.getSelectedOptions() ?? [];
2289
+ // Used to determine whether or not we need to fire an input / change event
2290
+ // which fire whenever the option element changes (value or selectedIndex)
2291
+ // on user interaction.
2292
+ let hasSelectedOptionChanged = false;
2293
+ if (selectedOptions.length) {
2294
+ const [firstSelectedOption] = selectedOptions[0];
2295
+ hasSelectedOptionChanged = this.lastSelectedOption !== firstSelectedOption;
2296
+ this.lastSelectedOption = firstSelectedOption;
2297
+ this[VALUE] = firstSelectedOption.value;
2298
+ this.displayText = firstSelectedOption.displayText;
2299
+ } else {
2300
+ hasSelectedOptionChanged = this.lastSelectedOption !== null;
2301
+ this.lastSelectedOption = null;
2302
+ this[VALUE] = '';
2303
+ this.displayText = '';
2304
+ }
2305
+ return hasSelectedOptionChanged;
2306
+ }
2307
+ /**
2308
+ * Focuses and activates the last selected item upon opening, and resets other
2309
+ * active items.
2310
+ */
2311
+ async handleOpening(e) {
2312
+ var _this$labelEl2, _this$labelEl2$remove, _getActiveItem;
2313
+ (_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');
2314
+ this.redispatchEvent(e);
2315
+ // FocusState.NONE means we want to handle focus ourselves and focus the
2316
+ // last selected item.
2317
+ if (this.defaultFocus !== FocusState.NONE) {
2318
+ return;
2319
+ }
2320
+ const items = this.menu.items;
2321
+ const activeItem = (_getActiveItem = getActiveItem(items)) === null || _getActiveItem === void 0 ? void 0 : _getActiveItem.item;
2322
+ let [selectedItem] = this.lastSelectedOptionRecords[0] ?? [null];
2323
+ // This is true if the user keys through the list but clicks out of the menu
2324
+ // thus no close-menu event is fired by an item and we can't clean up in
2325
+ // handleCloseMenu.
2326
+ if (activeItem && activeItem !== selectedItem) {
2327
+ activeItem.tabIndex = -1;
2328
+ }
2329
+ // in the case that nothing is selected, focus the first item
2330
+ selectedItem = selectedItem ?? items[0];
2331
+ if (selectedItem) {
2332
+ selectedItem.tabIndex = 0;
2333
+ selectedItem.focus();
2334
+ }
2335
+ }
2336
+ redispatchEvent(e) {
2337
+ redispatchEvent(this, e);
2338
+ }
2339
+ handleClosed(e) {
2340
+ this.open = false;
2341
+ this.redispatchEvent(e);
2342
+ }
2343
+ /**
2344
+ * Determines the reason for closing, and updates the UI accordingly.
2345
+ */
2346
+ handleCloseMenu(event) {
2347
+ const reason = event.detail.reason;
2348
+ const item = event.detail.itemPath[0];
2349
+ this.open = false;
2350
+ let hasChanged = false;
2351
+ if (reason.kind === 'click-selection') {
2352
+ hasChanged = this.selectItem(item);
2353
+ } else if (reason.kind === 'keydown' && isSelectableKey(reason.key)) {
2354
+ hasChanged = this.selectItem(item);
2355
+ } else {
2356
+ // This can happen on ESC being pressed
2357
+ item.tabIndex = -1;
2358
+ item.blur();
2359
+ }
2360
+ // Dispatch interaction events since selection has been made via keyboard
2361
+ // or mouse.
2362
+ if (hasChanged) {
2363
+ this.dispatchInteractionEvents();
2364
+ }
2365
+ }
2366
+ /**
2367
+ * Selects a given option, deselects other options, and updates the UI.
2368
+ *
2369
+ * @return Whether the last selected option has changed.
2370
+ */
2371
+ selectItem(item) {
2372
+ const selectedOptions = this.getSelectedOptions() ?? [];
2373
+ selectedOptions.forEach(([option]) => {
2374
+ if (item !== option) {
2375
+ option.selected = false;
2376
+ }
2377
+ });
2378
+ item.selected = true;
2379
+ return this.updateValueAndDisplayText();
2380
+ }
2381
+ /**
2382
+ * Handles updating selection when an option element requests selection via
2383
+ * property / attribute change.
2384
+ */
2385
+ handleRequestSelection(event) {
2386
+ const requestingOptionEl = event.target;
2387
+ // No-op if this item is already selected.
2388
+ if (this.lastSelectedOptionRecords.some(([option]) => option === requestingOptionEl)) {
2389
+ return;
2390
+ }
2391
+ this.selectItem(requestingOptionEl);
2392
+ }
2393
+ /**
2394
+ * Handles updating selection when an option element requests deselection via
2395
+ * property / attribute change.
2396
+ */
2397
+ handleRequestDeselection(event) {
2398
+ const requestingOptionEl = event.target;
2399
+ // No-op if this item is not even in the list of tracked selected items.
2400
+ if (!this.lastSelectedOptionRecords.some(([option]) => option === requestingOptionEl)) {
2401
+ return;
2402
+ }
2403
+ this.updateValueAndDisplayText();
2404
+ }
2405
+ /**
2406
+ * Attempts to initialize the selected option from user-settable values like
2407
+ * SSR, setting `value`, or `selectedIndex` at startup.
2408
+ */
2409
+ initUserSelection() {
2410
+ // User has set `.value` directly, but internals have not yet booted up.
2411
+ if (this.lastUserSetValue && !this.lastSelectedOptionRecords.length) {
2412
+ this.select(this.lastUserSetValue);
2413
+ // User has set `.selectedIndex` directly, but internals have not yet
2414
+ // booted up.
2415
+ } else if (this.lastUserSetSelectedIndex !== null && !this.lastSelectedOptionRecords.length) {
2416
+ this.selectIndex(this.lastUserSetSelectedIndex);
2417
+ // Regular boot up!
2418
+ } else {
2419
+ this.updateValueAndDisplayText();
2420
+ }
2421
+ }
2422
+ handleIconChange() {
2423
+ this.hasLeadingIcon = this.leadingIcons.length > 0;
2424
+ }
2425
+ /**
2426
+ * Dispatches the `input` and `change` events.
2427
+ */
2428
+ dispatchInteractionEvents() {
2429
+ this.dispatchEvent(new Event('input', {
2430
+ bubbles: true,
2431
+ composed: true
2432
+ }));
2433
+ this.dispatchEvent(new Event('change', {
2434
+ bubbles: true
2435
+ }));
2436
+ }
2437
+ getErrorText() {
2438
+ return this.error ? this.errorText : this.nativeErrorText;
2439
+ }
2440
+ [getFormValue]() {
2441
+ return this.value;
2442
+ }
2443
+ formResetCallback() {
2444
+ this.reset();
2445
+ }
2446
+ formStateRestoreCallback(state) {
2447
+ this.value = state;
2448
+ }
2449
+ click() {
2450
+ var _this$field2;
2451
+ (_this$field2 = this.field) === null || _this$field2 === void 0 || _this$field2.click();
2452
+ }
2453
+ [createValidator]() {
2454
+ return new SelectValidator(() => this);
2455
+ }
2456
+ [getValidityAnchor]() {
2457
+ return this.field;
2458
+ }
2459
+ }
2460
+ /** @nocollapse */
2461
+ Select.shadowRootOptions = {
2462
+ ...i.shadowRootOptions,
2463
+ delegatesFocus: true
2464
+ };
2465
+ __decorate([n$1({
2466
+ type: Boolean
2467
+ })], Select.prototype, "quick", void 0);
2468
+ __decorate([n$1({
2469
+ type: Boolean
2470
+ })], Select.prototype, "required", void 0);
2471
+ __decorate([n$1({
2472
+ type: String,
2473
+ attribute: 'error-text'
2474
+ })], Select.prototype, "errorText", void 0);
2475
+ __decorate([n$1()], Select.prototype, "label", void 0);
2476
+ __decorate([n$1({
2477
+ type: Boolean,
2478
+ attribute: 'no-asterisk'
2479
+ })], Select.prototype, "noAsterisk", void 0);
2480
+ __decorate([n$1({
2481
+ type: String,
2482
+ attribute: 'supporting-text'
2483
+ })], Select.prototype, "supportingText", void 0);
2484
+ __decorate([n$1({
2485
+ type: Boolean,
2486
+ reflect: true
2487
+ })], Select.prototype, "error", void 0);
2488
+ __decorate([n$1({
2489
+ attribute: 'menu-positioning'
2490
+ })], Select.prototype, "menuPositioning", void 0);
2491
+ __decorate([n$1({
2492
+ type: Boolean,
2493
+ attribute: 'clamp-menu-width'
2494
+ })], Select.prototype, "clampMenuWidth", void 0);
2495
+ __decorate([n$1({
2496
+ type: Number,
2497
+ attribute: 'typeahead-delay'
2498
+ })], Select.prototype, "typeaheadDelay", void 0);
2499
+ __decorate([n$1({
2500
+ type: Boolean,
2501
+ attribute: 'has-leading-icon'
2502
+ })], Select.prototype, "hasLeadingIcon", void 0);
2503
+ __decorate([n$1({
2504
+ attribute: 'display-text'
2505
+ })], Select.prototype, "displayText", void 0);
2506
+ __decorate([n$1({
2507
+ attribute: 'menu-align'
2508
+ })], Select.prototype, "menuAlign", void 0);
2509
+ __decorate([n$1()], Select.prototype, "value", null);
2510
+ __decorate([n$1({
2511
+ type: Number,
2512
+ attribute: 'selected-index'
2513
+ })], Select.prototype, "selectedIndex", null);
2514
+ __decorate([r()], Select.prototype, "nativeError", void 0);
2515
+ __decorate([r()], Select.prototype, "nativeErrorText", void 0);
2516
+ __decorate([r()], Select.prototype, "focused", void 0);
2517
+ __decorate([r()], Select.prototype, "open", void 0);
2518
+ __decorate([r()], Select.prototype, "defaultFocus", void 0);
2519
+ __decorate([e$1('.field')], Select.prototype, "field", void 0);
2520
+ __decorate([e$1('md-menu')], Select.prototype, "menu", void 0);
2521
+ __decorate([e$1('#label')], Select.prototype, "labelEl", void 0);
2522
+ __decorate([o({
2523
+ slot: 'leading-icon',
2524
+ flatten: true
2525
+ })], Select.prototype, "leadingIcons", void 0);
2526
+
2527
+ /**
2528
+ * @license
2529
+ * Copyright 2023 Google LLC
2530
+ * SPDX-License-Identifier: Apache-2.0
2531
+ */
2532
+ // tslint:disable-next-line:enforce-comments-on-exported-symbols
2533
+ class OutlinedSelect extends Select {
2534
+ constructor() {
2535
+ super(...arguments);
2536
+ this.fieldTag = i$2`md-outlined-field`;
2537
+ }
2538
+ }
2539
+
2540
+ /**
2541
+ * @license
2542
+ * Copyright 2024 Google LLC
2543
+ * SPDX-License-Identifier: Apache-2.0
2544
+ */
2545
+ // Generated stylesheet for ./select/internal/outlined-select-styles.css.
2546
+ 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)}
2547
+ `;
2548
+
2549
+ /**
2550
+ * @license
2551
+ * Copyright 2024 Google LLC
2552
+ * SPDX-License-Identifier: Apache-2.0
2553
+ */
2554
+ // Generated stylesheet for ./select/internal/shared-styles.css.
2555
+ 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}
2556
+ `;
2557
+
2558
+ /**
2559
+ * @license
2560
+ * Copyright 2023 Google LLC
2561
+ * SPDX-License-Identifier: Apache-2.0
2562
+ */
2563
+ /**
2564
+ * @summary
2565
+ * Select menus display a list of choices on temporary surfaces and display the
2566
+ * currently selected menu item above the menu.
2567
+ *
2568
+ * @description
2569
+ * The select component allows users to choose a value from a fixed list of
2570
+ * available options. Composed of an interactive anchor button and a menu, it is
2571
+ * analogous to the native HTML `<select>` element. This is the "outlined"
2572
+ * variant.
2573
+ *
2574
+ * @example
2575
+ * ```html
2576
+ * <md-outlined-select label="fruits">
2577
+ * <!-- An empty selected option will give select an "un-filled" state -->
2578
+ * <md-select-option selected></md-select-option>
2579
+ * <md-select-option value="apple" headline="Apple"></md-select-option>
2580
+ * <md-select-option value="banana" headline="Banana"></md-select-option>
2581
+ * <md-select-option value="kiwi" headline="Kiwi"></md-select-option>
2582
+ * <md-select-option value="orange" headline="Orange"></md-select-option>
2583
+ * <md-select-option value="tomato" headline="Tomato"></md-select-option>
2584
+ * </md-outlined-select>
2585
+ * ```
2586
+ *
2587
+ * @final
2588
+ * @suppress {visibility}
2589
+ */
2590
+ let MdOutlinedSelect = class MdOutlinedSelect extends OutlinedSelect {};
2591
+ MdOutlinedSelect.styles = [styles$1, styles$2];
2592
+ MdOutlinedSelect = __decorate([t('md-outlined-select')], MdOutlinedSelect);
2593
+
2594
+ /**
2595
+ * @license
2596
+ * Copyright 2024 Google LLC
2597
+ * SPDX-License-Identifier: Apache-2.0
2598
+ */
2599
+ // Generated stylesheet for ./menu/internal/menuitem/menu-item-styles.css.
2600
+ 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}}
2601
+ `;
2602
+
2603
+ /**
2604
+ * @license
2605
+ * Copyright 2023 Google LLC
2606
+ * SPDX-License-Identifier: Apache-2.0
2607
+ */
2608
+ /**
2609
+ * A controller that provides most functionality of an element that implements
2610
+ * the MenuItem interface.
2611
+ */
2612
+ class MenuItemController {
2613
+ /**
2614
+ * @param host The MenuItem in which to attach this controller to.
2615
+ * @param config The object that configures this controller's behavior.
2616
+ */
2617
+ constructor(host, config) {
2618
+ this.host = host;
2619
+ this.internalTypeaheadText = null;
2620
+ /**
2621
+ * Bind this click listener to the interactive element. Handles closing the
2622
+ * menu.
2623
+ */
2624
+ this.onClick = () => {
2625
+ if (this.host.keepOpen) return;
2626
+ this.host.dispatchEvent(createDefaultCloseMenuEvent(this.host, {
2627
+ kind: CloseReason.CLICK_SELECTION
2628
+ }));
2629
+ };
2630
+ /**
2631
+ * Bind this click listener to the interactive element. Handles closing the
2632
+ * menu.
2633
+ */
2634
+ this.onKeydown = event => {
2635
+ // Check if the interactive element is an anchor tag. If so, click it.
2636
+ if (this.host.href && event.code === 'Enter') {
2637
+ const interactiveElement = this.getInteractiveElement();
2638
+ if (interactiveElement instanceof HTMLAnchorElement) {
2639
+ interactiveElement.click();
2640
+ }
2641
+ }
2642
+ if (event.defaultPrevented) return;
2643
+ // If the host has keepOpen = true we should ignore clicks & Space/Enter,
2644
+ // however we always maintain the ability to close a menu with a explicit
2645
+ // `escape` keypress.
2646
+ const keyCode = event.code;
2647
+ if (this.host.keepOpen && keyCode !== 'Escape') return;
2648
+ if (isClosableKey(keyCode)) {
2649
+ event.preventDefault();
2650
+ this.host.dispatchEvent(createDefaultCloseMenuEvent(this.host, {
2651
+ kind: CloseReason.KEYDOWN,
2652
+ key: keyCode
2653
+ }));
2654
+ }
2655
+ };
2656
+ this.getHeadlineElements = config.getHeadlineElements;
2657
+ this.getSupportingTextElements = config.getSupportingTextElements;
2658
+ this.getDefaultElements = config.getDefaultElements;
2659
+ this.getInteractiveElement = config.getInteractiveElement;
2660
+ this.host.addController(this);
2661
+ }
2662
+ /**
2663
+ * The text that is selectable via typeahead. If not set, defaults to the
2664
+ * innerText of the item slotted into the `"headline"` slot, and if there are
2665
+ * no slotted elements into headline, then it checks the _default_ slot, and
2666
+ * then the `"supporting-text"` slot if nothing is in _default_.
2667
+ */
2668
+ get typeaheadText() {
2669
+ if (this.internalTypeaheadText !== null) {
2670
+ return this.internalTypeaheadText;
2671
+ }
2672
+ const headlineElements = this.getHeadlineElements();
2673
+ const textParts = [];
2674
+ headlineElements.forEach(headlineElement => {
2675
+ if (headlineElement.textContent && headlineElement.textContent.trim()) {
2676
+ textParts.push(headlineElement.textContent.trim());
2677
+ }
2678
+ });
2679
+ // If there are no headline elements, check the default slot's text content
2680
+ if (textParts.length === 0) {
2681
+ this.getDefaultElements().forEach(defaultElement => {
2682
+ if (defaultElement.textContent && defaultElement.textContent.trim()) {
2683
+ textParts.push(defaultElement.textContent.trim());
2684
+ }
2685
+ });
2686
+ }
2687
+ // If there are no headline nor default slot elements, check the
2688
+ //supporting-text slot's text content
2689
+ if (textParts.length === 0) {
2690
+ this.getSupportingTextElements().forEach(supportingTextElement => {
2691
+ if (supportingTextElement.textContent && supportingTextElement.textContent.trim()) {
2692
+ textParts.push(supportingTextElement.textContent.trim());
2693
+ }
2694
+ });
2695
+ }
2696
+ return textParts.join(' ');
2697
+ }
2698
+ /**
2699
+ * The recommended tag name to render as the list item.
2700
+ */
2701
+ get tagName() {
2702
+ const type = this.host.type;
2703
+ switch (type) {
2704
+ case 'link':
2705
+ return 'a';
2706
+ case 'button':
2707
+ return 'button';
2708
+ default:
2709
+ case 'menuitem':
2710
+ case 'option':
2711
+ return 'li';
2712
+ }
2713
+ }
2714
+ /**
2715
+ * The recommended role of the menu item.
2716
+ */
2717
+ get role() {
2718
+ return this.host.type === 'option' ? 'option' : 'menuitem';
2719
+ }
2720
+ hostConnected() {
2721
+ this.host.toggleAttribute('md-menu-item', true);
2722
+ }
2723
+ hostUpdate() {
2724
+ if (this.host.href) {
2725
+ this.host.type = 'link';
2726
+ }
2727
+ }
2728
+ /**
2729
+ * Use to set the typeaheadText when it changes.
2730
+ */
2731
+ setTypeaheadText(text) {
2732
+ this.internalTypeaheadText = text;
2733
+ }
2734
+ }
2735
+
2736
+ /**
2737
+ * @license
2738
+ * Copyright 2023 Google LLC
2739
+ * SPDX-License-Identifier: Apache-2.0
2740
+ */
2741
+ /**
2742
+ * Creates an event fired by a SelectOption to request selection from md-select.
2743
+ * Typically fired after `selected` changes from `false` to `true`.
2744
+ */
2745
+ function createRequestSelectionEvent() {
2746
+ return new Event('request-selection', {
2747
+ bubbles: true,
2748
+ composed: true
2749
+ });
2750
+ }
2751
+ /**
2752
+ * Creates an event fired by a SelectOption to request deselection from
2753
+ * md-select. Typically fired after `selected` changes from `true` to `false`.
2754
+ */
2755
+ function createRequestDeselectionEvent() {
2756
+ return new Event('request-deselection', {
2757
+ bubbles: true,
2758
+ composed: true
2759
+ });
2760
+ }
2761
+ /**
2762
+ * A controller that provides most functionality and md-select compatibility for
2763
+ * an element that implements the SelectOption interface.
2764
+ */
2765
+ class SelectOptionController {
2766
+ /**
2767
+ * The recommended role of the select option.
2768
+ */
2769
+ get role() {
2770
+ return this.menuItemController.role;
2771
+ }
2772
+ /**
2773
+ * The text that is selectable via typeahead. If not set, defaults to the
2774
+ * innerText of the item slotted into the `"headline"` slot, and if there are
2775
+ * no slotted elements into headline, then it checks the _default_ slot, and
2776
+ * then the `"supporting-text"` slot if nothing is in _default_.
2777
+ */
2778
+ get typeaheadText() {
2779
+ return this.menuItemController.typeaheadText;
2780
+ }
2781
+ setTypeaheadText(text) {
2782
+ this.menuItemController.setTypeaheadText(text);
2783
+ }
2784
+ /**
2785
+ * The text that is displayed in the select field when selected. If not set,
2786
+ * defaults to the textContent of the item slotted into the `"headline"` slot,
2787
+ * and if there are no slotted elements into headline, then it checks the
2788
+ * _default_ slot, and then the `"supporting-text"` slot if nothing is in
2789
+ * _default_.
2790
+ */
2791
+ get displayText() {
2792
+ if (this.internalDisplayText !== null) {
2793
+ return this.internalDisplayText;
2794
+ }
2795
+ return this.menuItemController.typeaheadText;
2796
+ }
2797
+ setDisplayText(text) {
2798
+ this.internalDisplayText = text;
2799
+ }
2800
+ /**
2801
+ * @param host The SelectOption in which to attach this controller to.
2802
+ * @param config The object that configures this controller's behavior.
2803
+ */
2804
+ constructor(host, config) {
2805
+ this.host = host;
2806
+ this.internalDisplayText = null;
2807
+ this.firstUpdate = true;
2808
+ /**
2809
+ * Bind this click listener to the interactive element. Handles closing the
2810
+ * menu.
2811
+ */
2812
+ this.onClick = () => {
2813
+ this.menuItemController.onClick();
2814
+ };
2815
+ /**
2816
+ * Bind this click listener to the interactive element. Handles closing the
2817
+ * menu.
2818
+ */
2819
+ this.onKeydown = e => {
2820
+ this.menuItemController.onKeydown(e);
2821
+ };
2822
+ this.lastSelected = this.host.selected;
2823
+ this.menuItemController = new MenuItemController(host, config);
2824
+ host.addController(this);
2825
+ }
2826
+ hostUpdate() {
2827
+ if (this.lastSelected !== this.host.selected) {
2828
+ this.host.ariaSelected = this.host.selected ? 'true' : 'false';
2829
+ }
2830
+ }
2831
+ hostUpdated() {
2832
+ // Do not dispatch event on first update / boot-up.
2833
+ if (this.lastSelected !== this.host.selected && !this.firstUpdate) {
2834
+ // This section is really useful for when the user sets selected on the
2835
+ // option programmatically. Most other cases (click and keyboard) are
2836
+ // handled by md-select because it needs to coordinate the
2837
+ // single-selection behavior.
2838
+ if (this.host.selected) {
2839
+ this.host.dispatchEvent(createRequestSelectionEvent());
2840
+ } else {
2841
+ this.host.dispatchEvent(createRequestDeselectionEvent());
2842
+ }
2843
+ }
2844
+ this.lastSelected = this.host.selected;
2845
+ this.firstUpdate = false;
2846
+ }
2847
+ }
2848
+
2849
+ /**
2850
+ * @license
2851
+ * Copyright 2023 Google LLC
2852
+ * SPDX-License-Identifier: Apache-2.0
2853
+ */
2854
+ // Separate variable needed for closure.
2855
+ const selectOptionBaseClass = mixinDelegatesAria(i);
2856
+ /**
2857
+ * @fires close-menu {CustomEvent<{initiator: SelectOption, reason: Reason, itemPath: SelectOption[]}>}
2858
+ * Closes the encapsulating menu on closable interaction. --bubbles --composed
2859
+ * @fires request-selection {Event} Requests the parent md-select to select this
2860
+ * element (and deselect others if single-selection) when `selected` changed to
2861
+ * `true`. --bubbles --composed
2862
+ * @fires request-deselection {Event} Requests the parent md-select to deselect
2863
+ * this element when `selected` changed to `false`. --bubbles --composed
2864
+ */
2865
+ class SelectOptionEl extends selectOptionBaseClass {
2866
+ constructor() {
2867
+ super(...arguments);
2868
+ /**
2869
+ * Disables the item and makes it non-selectable and non-interactive.
2870
+ */
2871
+ this.disabled = false;
2872
+ /**
2873
+ * READONLY: self-identifies as a menu item and sets its identifying attribute
2874
+ */
2875
+ this.isMenuItem = true;
2876
+ /**
2877
+ * Sets the item in the selected visual state when a submenu is opened.
2878
+ */
2879
+ this.selected = false;
2880
+ /**
2881
+ * Form value of the option.
2882
+ */
2883
+ this.value = '';
2884
+ this.type = 'option';
2885
+ this.selectOptionController = new SelectOptionController(this, {
2886
+ getHeadlineElements: () => {
2887
+ return this.headlineElements;
2888
+ },
2889
+ getSupportingTextElements: () => {
2890
+ return this.supportingTextElements;
2891
+ },
2892
+ getDefaultElements: () => {
2893
+ return this.defaultElements;
2894
+ },
2895
+ getInteractiveElement: () => this.listItemRoot
2896
+ });
2897
+ }
2898
+ /**
2899
+ * The text that is selectable via typeahead. If not set, defaults to the
2900
+ * innerText of the item slotted into the `"headline"` slot.
2901
+ */
2902
+ get typeaheadText() {
2903
+ return this.selectOptionController.typeaheadText;
2904
+ }
2905
+ set typeaheadText(text) {
2906
+ this.selectOptionController.setTypeaheadText(text);
2907
+ }
2908
+ /**
2909
+ * The text that is displayed in the select field when selected. If not set,
2910
+ * defaults to the textContent of the item slotted into the `"headline"` slot.
2911
+ */
2912
+ get displayText() {
2913
+ return this.selectOptionController.displayText;
2914
+ }
2915
+ set displayText(text) {
2916
+ this.selectOptionController.setDisplayText(text);
2917
+ }
2918
+ render() {
2919
+ return this.renderListItem(b`
2920
+ <md-item>
2921
+ <div slot="container">
2922
+ ${this.renderRipple()} ${this.renderFocusRing()}
2923
+ </div>
2924
+ <slot name="start" slot="start"></slot>
2925
+ <slot name="end" slot="end"></slot>
2926
+ ${this.renderBody()}
2927
+ </md-item>
2928
+ `);
2929
+ }
2930
+ /**
2931
+ * Renders the root list item.
2932
+ *
2933
+ * @param content the child content of the list item.
2934
+ */
2935
+ renderListItem(content) {
2936
+ return b`
2937
+ <li
2938
+ id="item"
2939
+ tabindex=${this.disabled ? -1 : 0}
2940
+ role=${this.selectOptionController.role}
2941
+ aria-label=${this.ariaLabel || A}
2942
+ aria-selected=${this.ariaSelected || A}
2943
+ aria-checked=${this.ariaChecked || A}
2944
+ aria-expanded=${this.ariaExpanded || A}
2945
+ aria-haspopup=${this.ariaHasPopup || A}
2946
+ class="list-item ${e$2(this.getRenderClasses())}"
2947
+ @click=${this.selectOptionController.onClick}
2948
+ @keydown=${this.selectOptionController.onKeydown}
2949
+ >${content}</li
2950
+ >
2951
+ `;
2952
+ }
2953
+ /**
2954
+ * Handles rendering of the ripple element.
2955
+ */
2956
+ renderRipple() {
2957
+ return b` <md-ripple
2958
+ part="ripple"
2959
+ for="item"
2960
+ ?disabled=${this.disabled}></md-ripple>`;
2961
+ }
2962
+ /**
2963
+ * Handles rendering of the focus ring.
2964
+ */
2965
+ renderFocusRing() {
2966
+ return b` <md-focus-ring
2967
+ part="focus-ring"
2968
+ for="item"
2969
+ inward></md-focus-ring>`;
2970
+ }
2971
+ /**
2972
+ * Classes applied to the list item root.
2973
+ */
2974
+ getRenderClasses() {
2975
+ return {
2976
+ 'disabled': this.disabled,
2977
+ 'selected': this.selected
2978
+ };
2979
+ }
2980
+ /**
2981
+ * Handles rendering the headline and supporting text.
2982
+ */
2983
+ renderBody() {
2984
+ return b`
2985
+ <slot></slot>
2986
+ <slot name="overline" slot="overline"></slot>
2987
+ <slot name="headline" slot="headline"></slot>
2988
+ <slot name="supporting-text" slot="supporting-text"></slot>
2989
+ <slot
2990
+ name="trailing-supporting-text"
2991
+ slot="trailing-supporting-text"></slot>
2992
+ `;
2993
+ }
2994
+ focus() {
2995
+ var _this$listItemRoot;
2996
+ // TODO(b/300334509): needed for some cases where delegatesFocus doesn't
2997
+ // work programmatically like in FF and select-option
2998
+ (_this$listItemRoot = this.listItemRoot) === null || _this$listItemRoot === void 0 || _this$listItemRoot.focus();
2999
+ }
3000
+ }
3001
+ /** @nocollapse */
3002
+ SelectOptionEl.shadowRootOptions = {
3003
+ ...i.shadowRootOptions,
3004
+ delegatesFocus: true
3005
+ };
3006
+ __decorate([n$1({
3007
+ type: Boolean,
3008
+ reflect: true
3009
+ })], SelectOptionEl.prototype, "disabled", void 0);
3010
+ __decorate([n$1({
3011
+ type: Boolean,
3012
+ attribute: 'md-menu-item',
3013
+ reflect: true
3014
+ })], SelectOptionEl.prototype, "isMenuItem", void 0);
3015
+ __decorate([n$1({
3016
+ type: Boolean
3017
+ })], SelectOptionEl.prototype, "selected", void 0);
3018
+ __decorate([n$1()], SelectOptionEl.prototype, "value", void 0);
3019
+ __decorate([e$1('.list-item')], SelectOptionEl.prototype, "listItemRoot", void 0);
3020
+ __decorate([o({
3021
+ slot: 'headline'
3022
+ })], SelectOptionEl.prototype, "headlineElements", void 0);
3023
+ __decorate([o({
3024
+ slot: 'supporting-text'
3025
+ })], SelectOptionEl.prototype, "supportingTextElements", void 0);
3026
+ __decorate([n({
3027
+ slot: ''
3028
+ })], SelectOptionEl.prototype, "defaultElements", void 0);
3029
+ __decorate([n$1({
3030
+ attribute: 'typeahead-text'
3031
+ })], SelectOptionEl.prototype, "typeaheadText", null);
3032
+ __decorate([n$1({
3033
+ attribute: 'display-text'
3034
+ })], SelectOptionEl.prototype, "displayText", null);
3035
+
3036
+ /**
3037
+ * @license
3038
+ * Copyright 2023 Google LLC
3039
+ * SPDX-License-Identifier: Apache-2.0
3040
+ */
3041
+ /**
3042
+ * @summary
3043
+ * Select menus display a list of choices on temporary surfaces and display the
3044
+ * currently selected menu item above the menu.
3045
+ *
3046
+ * @description
3047
+ * The select component allows users to choose a value from a fixed list of
3048
+ * available options. Composed of an interactive anchor button and a menu, it is
3049
+ * analogous to the native HTML `<select>` element. This is the option that
3050
+ * can be placed inside of an md-select.
3051
+ *
3052
+ * This component is a subclass of `md-menu-item` and can accept the same slots,
3053
+ * properties, and events as `md-menu-item`.
3054
+ *
3055
+ * @example
3056
+ * ```html
3057
+ * <md-outlined-select label="fruits">
3058
+ * <!-- An empty selected option will give select an "un-filled" state -->
3059
+ * <md-select-option selected></md-select-option>
3060
+ * <md-select-option value="apple" headline="Apple"></md-select-option>
3061
+ * <md-select-option value="banana" headline="Banana"></md-select-option>
3062
+ * <md-select-option value="kiwi" headline="Kiwi"></md-select-option>
3063
+ * <md-select-option value="orange" headline="Orange"></md-select-option>
3064
+ * <md-select-option value="tomato" headline="Tomato"></md-select-option>
3065
+ * </md-outlined-select>
3066
+ * ```
3067
+ *
3068
+ * @final
3069
+ * @suppress {visibility}
3070
+ */
3071
+ let MdSelectOption = class MdSelectOption extends SelectOptionEl {};
3072
+ MdSelectOption.styles = [styles];
3073
+ MdSelectOption = __decorate([t('md-select-option')], MdSelectOption);
3074
+
3075
+ var __defProp = Object.defineProperty;
3076
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3077
+ var __decorateClass = (decorators, target, key, kind) => {
3078
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
3079
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
3080
+ if (kind && result) __defProp(target, key, result);
3081
+ return result;
3082
+ };
3083
+ const LOG_LEVELS = [{
3084
+ value: "critical",
3085
+ label: "Critical"
3086
+ }, {
3087
+ value: "error",
3088
+ label: "Error"
3089
+ }, {
3090
+ value: "warning",
3091
+ label: "Warning"
3092
+ }, {
3093
+ value: "info",
3094
+ label: "Info"
3095
+ }, {
3096
+ value: "debug",
3097
+ label: "Debug"
3098
+ }];
3099
+ let LogLevelDialog = class extends i {
3100
+ constructor() {
3101
+ super(...arguments);
3102
+ this._consoleLevel = "info";
3103
+ this._fileLevel = null;
3104
+ this._loading = true;
3105
+ this._applying = false;
3106
+ }
3107
+ connectedCallback() {
3108
+ super.connectedCallback();
3109
+ void this._loadLogLevels();
3110
+ }
3111
+ async _loadLogLevels() {
3112
+ try {
3113
+ const result = await this.client.getLogLevel();
3114
+ this._consoleLevel = result.console_loglevel;
3115
+ this._fileLevel = result.file_loglevel;
3116
+ } catch (err) {
3117
+ console.error("Failed to load log levels:", err);
3118
+ } finally {
3119
+ this._loading = false;
3120
+ }
3121
+ }
3122
+ async _apply() {
3123
+ this._applying = true;
3124
+ try {
3125
+ var _this$_fileSelect;
3126
+ const consoleLevel = this._consoleSelect.value;
3127
+ const fileLevel = (_this$_fileSelect = this._fileSelect) === null || _this$_fileSelect === void 0 ? void 0 : _this$_fileSelect.value;
3128
+ const result = await this.client.setLogLevel(consoleLevel, this._fileLevel !== null ? fileLevel : void 0);
3129
+ this._consoleLevel = result.console_loglevel;
3130
+ this._fileLevel = result.file_loglevel;
3131
+ this._close();
3132
+ } catch (err) {
3133
+ console.error("Failed to apply log levels:", err);
3134
+ alert("Failed to apply log levels");
3135
+ } finally {
3136
+ this._applying = false;
3137
+ }
3138
+ }
3139
+ _close() {
3140
+ this.shadowRoot.querySelector("md-dialog").close();
3141
+ }
3142
+ _handleClosed() {
3143
+ this.parentNode.removeChild(this);
3144
+ }
3145
+ render() {
3146
+ return b`
3147
+ <md-dialog open @cancel=${preventDefault} @closed=${this._handleClosed}>
3148
+ <div slot="headline">Server Log Settings</div>
3149
+ <div slot="content">
3150
+ ${this._loading ? b`<p class="loading">Loading...</p>` : b`
3151
+ <p class="hint">Changes are temporary and will be reset on the next server restart.</p>
3152
+ <div class="form-field">
3153
+ <label>Console Log Level</label>
3154
+ <md-outlined-select name="console" .value=${this._consoleLevel}>
3155
+ ${LOG_LEVELS.map(level => b`
3156
+ <md-select-option
3157
+ value=${level.value}
3158
+ ?selected=${level.value === this._consoleLevel}
3159
+ >
3160
+ <div slot="headline">${level.label}</div>
3161
+ </md-select-option>
3162
+ `)}
3163
+ </md-outlined-select>
3164
+ </div>
3165
+ ${this._fileLevel !== null ? b`
3166
+ <div class="form-field">
3167
+ <label>File Log Level</label>
3168
+ <md-outlined-select name="file" .value=${this._fileLevel}>
3169
+ ${LOG_LEVELS.map(level => b`
3170
+ <md-select-option
3171
+ value=${level.value}
3172
+ ?selected=${level.value === this._fileLevel}
3173
+ >
3174
+ <div slot="headline">${level.label}</div>
3175
+ </md-select-option>
3176
+ `)}
3177
+ </md-outlined-select>
3178
+ </div>
3179
+ ` : A}
3180
+ `}
3181
+ </div>
3182
+ <div slot="actions">
3183
+ <md-text-button @click=${this._close}>Cancel</md-text-button>
3184
+ <md-text-button @click=${this._apply} ?disabled=${this._loading || this._applying}>
3185
+ ${this._applying ? "Applying..." : "Apply"}
3186
+ </md-text-button>
3187
+ </div>
3188
+ </md-dialog>
3189
+ `;
3190
+ }
3191
+ };
3192
+ LogLevelDialog.styles = i$1`
3193
+ .loading {
3194
+ text-align: center;
3195
+ padding: 24px;
3196
+ color: var(--md-sys-color-on-surface-variant);
3197
+ }
3198
+
3199
+ .hint {
3200
+ font-size: 0.875rem;
3201
+ color: var(--md-sys-color-on-surface-variant);
3202
+ margin: 0 0 16px 0;
3203
+ font-style: italic;
3204
+ }
3205
+
3206
+ .form-field {
3207
+ margin-bottom: 16px;
3208
+ }
3209
+
3210
+ .form-field label {
3211
+ display: block;
3212
+ margin-bottom: 8px;
3213
+ font-weight: 500;
3214
+ color: var(--md-sys-color-on-surface);
3215
+ }
3216
+
3217
+ md-outlined-select {
3218
+ width: 100%;
3219
+ }
3220
+ `;
3221
+ __decorateClass([n$1({
3222
+ attribute: false
3223
+ })], LogLevelDialog.prototype, "client", 2);
3224
+ __decorateClass([r()], LogLevelDialog.prototype, "_consoleLevel", 2);
3225
+ __decorateClass([r()], LogLevelDialog.prototype, "_fileLevel", 2);
3226
+ __decorateClass([r()], LogLevelDialog.prototype, "_loading", 2);
3227
+ __decorateClass([r()], LogLevelDialog.prototype, "_applying", 2);
3228
+ __decorateClass([e$1("md-outlined-select[name='console']")], LogLevelDialog.prototype, "_consoleSelect", 2);
3229
+ __decorateClass([e$1("md-outlined-select[name='file']")], LogLevelDialog.prototype, "_fileSelect", 2);
3230
+ LogLevelDialog = __decorateClass([t("log-level-dialog")], LogLevelDialog);
3231
+
3232
+ export { LogLevelDialog };