@aurodesignsystem-dev/auro-formkit 0.0.0-pr1541.4 → 0.0.0-pr1542.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/components/checkbox/demo/customize.min.js +1 -1
  2. package/components/checkbox/demo/getting-started.min.js +1 -1
  3. package/components/checkbox/demo/index.min.js +1 -1
  4. package/components/checkbox/dist/index.js +1 -1
  5. package/components/checkbox/dist/registered.js +1 -1
  6. package/components/combobox/demo/api.md +2 -2
  7. package/components/combobox/demo/customize.md +1 -1
  8. package/components/combobox/demo/customize.min.js +40 -90
  9. package/components/combobox/demo/getting-started.min.js +40 -90
  10. package/components/combobox/demo/index.md +0 -1
  11. package/components/combobox/demo/index.min.js +40 -90
  12. package/components/combobox/demo/keyboard-behavior.md +1 -4
  13. package/components/combobox/dist/auro-combobox.d.ts +3 -1
  14. package/components/combobox/dist/index.js +23 -4
  15. package/components/combobox/dist/registered.js +23 -4
  16. package/components/counter/demo/customize.min.js +2 -2
  17. package/components/counter/demo/index.min.js +2 -2
  18. package/components/counter/dist/index.js +2 -2
  19. package/components/counter/dist/registered.js +2 -2
  20. package/components/datepicker/demo/customize.min.js +3 -3
  21. package/components/datepicker/demo/index.min.js +3 -3
  22. package/components/datepicker/dist/index.js +3 -3
  23. package/components/datepicker/dist/registered.js +3 -3
  24. package/components/dropdown/demo/customize.min.js +1 -1
  25. package/components/dropdown/demo/getting-started.min.js +1 -1
  26. package/components/dropdown/demo/index.min.js +1 -1
  27. package/components/dropdown/dist/index.js +1 -1
  28. package/components/dropdown/dist/registered.js +1 -1
  29. package/components/form/demo/customize.min.js +51 -105
  30. package/components/form/demo/getting-started.min.js +51 -105
  31. package/components/form/demo/index.min.js +51 -105
  32. package/components/form/demo/registerDemoDeps.min.js +51 -105
  33. package/components/input/demo/customize.min.js +1 -1
  34. package/components/input/demo/getting-started.min.js +1 -1
  35. package/components/input/demo/index.min.js +1 -1
  36. package/components/input/dist/index.js +1 -1
  37. package/components/input/dist/registered.js +1 -1
  38. package/components/menu/demo/accessibility.md +4 -4
  39. package/components/menu/demo/api.md +19 -26
  40. package/components/menu/demo/css-only.md +19 -26
  41. package/components/menu/demo/customize.md +47 -307
  42. package/components/menu/demo/design.md +1 -1
  43. package/components/menu/demo/getting-started.md +7 -144
  44. package/components/menu/demo/index.min.js +18 -213
  45. package/components/menu/demo/keyboard-behavior.md +4 -83
  46. package/components/menu/demo/voiceover.md +3 -3
  47. package/components/menu/demo/why-menu.md +9 -8
  48. package/components/menu/dist/auro-menu-utils.d.ts +0 -19
  49. package/components/menu/dist/auro-menu.d.ts +7 -3
  50. package/components/menu/dist/auro-menuoption.d.ts +0 -12
  51. package/components/menu/dist/index.js +17 -86
  52. package/components/menu/dist/registered.js +17 -86
  53. package/components/radio/demo/customize.min.js +1 -1
  54. package/components/radio/demo/getting-started.min.js +1 -1
  55. package/components/radio/demo/index.min.js +1 -1
  56. package/components/radio/dist/index.js +1 -1
  57. package/components/radio/dist/registered.js +1 -1
  58. package/components/select/demo/api.md +2 -2
  59. package/components/select/demo/customize.md +2 -5
  60. package/components/select/demo/customize.min.js +20 -93
  61. package/components/select/demo/design.md +10 -10
  62. package/components/select/demo/getting-started.md +1 -1
  63. package/components/select/demo/getting-started.min.js +20 -93
  64. package/components/select/demo/index.md +2 -2
  65. package/components/select/demo/index.min.js +20 -93
  66. package/components/select/demo/keyboard-behavior.md +18 -49
  67. package/components/select/dist/auro-select.d.ts +2 -6
  68. package/components/select/dist/index.js +3 -7
  69. package/components/select/dist/registered.js +3 -7
  70. package/custom-elements.json +1514 -1599
  71. package/package.json +1 -1
@@ -22,13 +22,10 @@
22
22
  <auro-anchorlink fluid href="#options" class="level2 body-xs">options</auro-anchorlink>
23
23
  <auro-anchorlink fluid href="#index" class="level2 body-xs">index</auro-anchorlink>
24
24
  <auro-anchorlink fluid href="#functions">Functions</auro-anchorlink>
25
+ <auro-anchorlink fluid href="#makeSelection" class="level2 body-xs">makeSelection()</auro-anchorlink>
25
26
  <auro-anchorlink fluid href="#reset" class="level2 body-xs">reset()</auro-anchorlink>
26
- <auro-anchorlink fluid href="#selectByValue" class="level2 body-xs">selectByValue()</auro-anchorlink>
27
27
  <auro-anchorlink fluid href="#updateActiveOption" class="level2 body-xs">updateActiveOption()</auro-anchorlink>
28
28
  <auro-anchorlink fluid href="#navigateOptions" class="level2 body-xs">navigateOptions()</auro-anchorlink>
29
- <auro-anchorlink fluid href="#events">Events</auro-anchorlink>
30
- <auro-anchorlink fluid href="#eventList" class="level2 body-xs">Event List</auro-anchorlink>
31
- <auro-anchorlink fluid href="#eventAttribute" class="level2 body-xs">Option event attribute</auro-anchorlink>
32
29
  </auro-nav>
33
30
  </nav>
34
31
  <div class="mainContent">
@@ -296,7 +293,7 @@ Ensure your `tsconfig.json` uses `"moduleResolution": "bundler"` so TypeScript c
296
293
  <auro-header level="2" id="stateManagement">State Management</auro-header>
297
294
  <p>The following properties reflect the current state of the menu and can be accessed via JavaScript.</p>
298
295
  <auro-header level="3" id="value">value</auro-header>
299
- <p>Gets or sets the selected value. In multi-select mode, this is a JSON stringified array of selected option values (e.g., <code>'["stops","duration"]'</code>). If the value matches an option marked <code>disabled</code> or <code>static</code>, the selection is cleared (<code>optionSelected</code> becomes <code>undefined</code>) and <code>auroMenu-selectValueFailure</code> is dispatched. <code>hidden</code> options remain selectable by value.</p>
296
+ <p>Gets or sets the selected value. In multi-select mode, this is a JSON stringified array of selected option values (e.g., <code>'["stops","duration"]'</code>).</p>
300
297
  <auro-header level="3" id="optionSelected">optionSelected</auro-header>
301
298
  <p>Returns the currently selected <code>&lt;auro-menuoption&gt;</code> element, or <code>undefined</code> if no option is selected. When <code>multiSelect</code> is enabled, returns an array of selected elements.</p>
302
299
  <auro-header level="3" id="optionActive">optionActive</auro-header>
@@ -311,148 +308,14 @@ Ensure your `tsconfig.json` uses `"moduleResolution": "bundler"` so TypeScript c
311
308
  <section>
312
309
  <auro-header level="2" id="functions">Functions</auro-header>
313
310
  <p>The following public methods are available on the <code>&lt;auro-menu&gt;</code> element.</p>
311
+ <auro-header level="3" id="makeSelection">makeSelection()</auro-header>
312
+ <p>Selects the currently active menu option. This is the programmatic equivalent of clicking on an option or pressing Enter while an option is focused.</p>
314
313
  <auro-header level="3" id="reset">reset()</auro-header>
315
314
  <p>Resets the menu to its initial state, clearing all selected options and restoring the value to <code>undefined</code>.</p>
316
- <div class="exampleWrapper">
317
- <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../apiExamples/reset.html) -->
318
- <!-- The below content is automatically added from ./../apiExamples/reset.html -->
319
- <auro-menu id="resetExample" value="duration">
320
- <auro-menuoption value="stops">Stops</auro-menuoption>
321
- <auro-menuoption value="price">Price</auro-menuoption>
322
- <auro-menuoption value="duration">Duration</auro-menuoption>
323
- <auro-menuoption value="departure">Departure</auro-menuoption>
324
- <auro-menuoption value="arrival">Arrival</auro-menuoption>
325
- </auro-menu>
326
- <br/><br/>
327
- <auro-button id="resetExampleBtn">RESET</auro-button>
328
- <!-- AURO-GENERATED-CONTENT:END -->
329
- </div>
330
- <auro-accordion alignRight>
331
- <span slot="trigger">See code</span>
332
- <!-- AURO-GENERATED-CONTENT:START (CODE:src=./../apiExamples/reset.html) -->
333
- <!-- The below code snippet is automatically added from ./../apiExamples/reset.html -->
334
- <pre class="language-html"><code class="language-html">&lt;auro-menu id="resetExample" value="duration"&gt;
335
- &lt;auro-menuoption value="stops"&gt;Stops&lt;/auro-menuoption&gt;
336
- &lt;auro-menuoption value="price"&gt;Price&lt;/auro-menuoption&gt;
337
- &lt;auro-menuoption value="duration"&gt;Duration&lt;/auro-menuoption&gt;
338
- &lt;auro-menuoption value="departure"&gt;Departure&lt;/auro-menuoption&gt;
339
- &lt;auro-menuoption value="arrival"&gt;Arrival&lt;/auro-menuoption&gt;
340
- &lt;/auro-menu&gt;
341
- &lt;br/&gt;&lt;br/&gt;
342
- &lt;auro-button id="resetExampleBtn"&gt;RESET&lt;/auro-button&gt;</code></pre>
343
- <!-- AURO-GENERATED-CONTENT:END -->
344
- </auro-accordion>
345
- <auro-header level="3" id="selectByValue">selectByValue(value)</auro-header>
346
- <p>Selects the option(s) whose <code>value</code> matches the argument. Accepts a string in single-select mode or an array of strings in multi-select mode; passing <code>undefined</code>, <code>null</code>, an empty string, or an empty array clears the selection.</p>
347
- <div class="exampleWrapper">
348
- <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../apiExamples/select-by-value.html) -->
349
- <!-- The below content is automatically added from ./../apiExamples/select-by-value.html -->
350
- <auro-menu id="selectByValueMenu">
351
- <auro-menuoption value="stops">Stops</auro-menuoption>
352
- <auro-menuoption value="price">Price</auro-menuoption>
353
- <auro-menuoption value="duration">Duration</auro-menuoption>
354
- <auro-menuoption value="departure">Departure</auro-menuoption>
355
- <auro-menuoption value="arrival">Arrival</auro-menuoption>
356
- </auro-menu>
357
- <br />
358
- <auro-button id="selectByValueStops">Select "Stops"</auro-button>
359
- <auro-button id="selectByValueDuration">Select "Duration"</auro-button>
360
- <auro-button id="selectByValueClear">Clear selection</auro-button>
361
- <!-- AURO-GENERATED-CONTENT:END -->
362
- </div>
363
- <auro-accordion alignRight>
364
- <span slot="trigger">See code</span>
365
- <!-- AURO-GENERATED-CONTENT:START (CODE:src=./../apiExamples/select-by-value.html) -->
366
- <!-- The below code snippet is automatically added from ./../apiExamples/select-by-value.html -->
367
- <pre class="language-html"><code class="language-html">&lt;auro-menu id="selectByValueMenu"&gt;
368
- &lt;auro-menuoption value="stops"&gt;Stops&lt;/auro-menuoption&gt;
369
- &lt;auro-menuoption value="price"&gt;Price&lt;/auro-menuoption&gt;
370
- &lt;auro-menuoption value="duration"&gt;Duration&lt;/auro-menuoption&gt;
371
- &lt;auro-menuoption value="departure"&gt;Departure&lt;/auro-menuoption&gt;
372
- &lt;auro-menuoption value="arrival"&gt;Arrival&lt;/auro-menuoption&gt;
373
- &lt;/auro-menu&gt;
374
- &lt;br /&gt;
375
- &lt;auro-button id="selectByValueStops"&gt;Select "Stops"&lt;/auro-button&gt;
376
- &lt;auro-button id="selectByValueDuration"&gt;Select "Duration"&lt;/auro-button&gt;
377
- &lt;auro-button id="selectByValueClear"&gt;Clear selection&lt;/auro-button&gt;</code></pre>
378
- <!-- AURO-GENERATED-CONTENT:END -->
379
- </auro-accordion>
380
- <auro-header level="3" id="updateActiveOption">updateActiveOption(indexOrOption)</auro-header>
381
- <p>Sets the specified option as the currently active (highlighted) option. Accepts either a numeric index into <code>options</code> or an <code>&lt;auro-menuoption&gt;</code> element reference.</p>
315
+ <auro-header level="3" id="updateActiveOption">updateActiveOption(option)</auro-header>
316
+ <p>Sets the specified <code>&lt;auro-menuoption&gt;</code> element as the currently active (highlighted) option.</p>
382
317
  <auro-header level="3" id="navigateOptions">navigateOptions(direction)</auro-header>
383
- <p>Moves the active option to the next or previous option. Accepts <code>'up'</code> or <code>'down'</code> as the direction parameter.</p>
384
- </section>
385
- <section>
386
- <auro-header level="2" id="events">Events</auro-header>
387
- <p>The <code>&lt;auro-menu&gt;</code> element dispatches custom events that consumers can subscribe to via <code>addEventListener</code>. The example below wires listeners for the full event set and logs each firing.</p>
388
- <auro-header level="3" id="eventList">Event List</auro-header>
389
- <ul>
390
- <li><code>auroMenu-selectedOption</code> — a new selection has been made.</li>
391
- <li><code>auroMenu-activatedOption</code> — the active (highlighted) option changed via keyboard or hover.</li>
392
- <li><code>auroMenu-optionsChange</code> — the set of available options was updated (e.g., after a slot change).</li>
393
- <li><code>auroMenu-selectValueReset</code> — the menu's <code>value</code> was reset via <code>reset()</code>.</li>
394
- <li><code>auroMenu-selectValueFailure</code> — a value selection (via <code>selectByValue()</code>, the <code>value</code> attribute, or setting the <code>value</code> property) found no matching selectable option. This includes values that match an option marked <code>disabled</code> or <code>static</code>, which are never selectable.</li>
395
- </ul>
396
- <div class="exampleWrapper">
397
- <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../apiExamples/events.html) -->
398
- <!-- The below content is automatically added from ./../apiExamples/events.html -->
399
- <auro-menu id="eventsExampleMenu">
400
- <auro-menuoption value="stops">Stops</auro-menuoption>
401
- <auro-menuoption value="price">Price</auro-menuoption>
402
- <auro-menuoption value="duration">Duration</auro-menuoption>
403
- <auro-menuoption value="departure">Departure</auro-menuoption>
404
- <auro-menuoption value="arrival">Arrival</auro-menuoption>
405
- </auro-menu>
406
- <br />
407
- <auro-button id="eventsExampleReset">reset()</auro-button>
408
- <auro-button id="eventsExampleBadValue">selectByValue("nonexistent")</auro-button>
409
- <auro-button id="eventsExampleClearLog">Clear log</auro-button>
410
- <pre id="eventsExampleLog" style="max-height: 200px; overflow: auto; padding: var(--ds-size-100, 8px); background: var(--ds-color-container-secondary, #f5f5f5); border-radius: 4px;">(interact with the menu above)</pre>
411
- <!-- AURO-GENERATED-CONTENT:END -->
412
- </div>
413
- <auro-accordion alignRight>
414
- <span slot="trigger">See code</span>
415
- <!-- AURO-GENERATED-CONTENT:START (CODE:src=./../apiExamples/events.html) -->
416
- <!-- The below code snippet is automatically added from ./../apiExamples/events.html -->
417
- <pre class="language-html"><code class="language-html">&lt;auro-menu id="eventsExampleMenu"&gt;
418
- &lt;auro-menuoption value="stops"&gt;Stops&lt;/auro-menuoption&gt;
419
- &lt;auro-menuoption value="price"&gt;Price&lt;/auro-menuoption&gt;
420
- &lt;auro-menuoption value="duration"&gt;Duration&lt;/auro-menuoption&gt;
421
- &lt;auro-menuoption value="departure"&gt;Departure&lt;/auro-menuoption&gt;
422
- &lt;auro-menuoption value="arrival"&gt;Arrival&lt;/auro-menuoption&gt;
423
- &lt;/auro-menu&gt;
424
- &lt;br /&gt;
425
- &lt;auro-button id="eventsExampleReset"&gt;reset()&lt;/auro-button&gt;
426
- &lt;auro-button id="eventsExampleBadValue"&gt;selectByValue("nonexistent")&lt;/auro-button&gt;
427
- &lt;auro-button id="eventsExampleClearLog"&gt;Clear log&lt;/auro-button&gt;
428
- &lt;pre id="eventsExampleLog" style="max-height: 200px; overflow: auto; padding: var(--ds-size-100, 8px); background: var(--ds-color-container-secondary, #f5f5f5); border-radius: 4px;"&gt;(interact with the menu above)&lt;/pre&gt;</code></pre>
429
- <!-- AURO-GENERATED-CONTENT:END -->
430
- </auro-accordion>
431
- <auro-header level="3" id="eventAttribute">Option event attribute</auro-header>
432
- <p>Attach an <code>event</code> attribute to any <code>auro-menuoption</code> to dispatch a named custom event from the menu when that option is selected. The generic <code>auroMenu-customEventFired</code> event is also dispatched on every firing — its <code>detail.option</code> references the option that triggered it, so a single listener can route on the <code>event</code> attribute value.</p>
433
- <div class="exampleWrapper">
434
- <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../apiExamples/event-attribute.html) -->
435
- <!-- The below content is automatically added from ./../apiExamples/event-attribute.html -->
436
- <auro-menu id="eventAttrExample">
437
- <auro-menuoption value="search" event="custom-flight-search">Search flights</auro-menuoption>
438
- <auro-menuoption value="clear" event="custom-clear-filters">Clear filters</auro-menuoption>
439
- <auro-menuoption value="save" event="custom-save-preferences">Save preferences</auro-menuoption>
440
- </auro-menu>
441
- <p>Last custom event: <output id="eventAttrOutput">(none)</output></p>
442
- <!-- AURO-GENERATED-CONTENT:END -->
443
- </div>
444
- <auro-accordion alignRight>
445
- <span slot="trigger">See code</span>
446
- <!-- AURO-GENERATED-CONTENT:START (CODE:src=./../apiExamples/event-attribute.html) -->
447
- <!-- The below code snippet is automatically added from ./../apiExamples/event-attribute.html -->
448
- <pre class="language-html"><code class="language-html">&lt;auro-menu id="eventAttrExample"&gt;
449
- &lt;auro-menuoption value="search" event="custom-flight-search"&gt;Search flights&lt;/auro-menuoption&gt;
450
- &lt;auro-menuoption value="clear" event="custom-clear-filters"&gt;Clear filters&lt;/auro-menuoption&gt;
451
- &lt;auro-menuoption value="save" event="custom-save-preferences"&gt;Save preferences&lt;/auro-menuoption&gt;
452
- &lt;/auro-menu&gt;
453
- &lt;p&gt;Last custom event: &lt;output id="eventAttrOutput"&gt;(none)&lt;/output&gt;&lt;/p&gt;</code></pre>
454
- <!-- AURO-GENERATED-CONTENT:END -->
455
- </auro-accordion>
318
+ <p>Moves the highlight to the next or previous option. Accepts <code>'up'</code> or <code>'down'</code> as the direction parameter.</p>
456
319
  </section>
457
320
  </div>
458
321
  </div>
@@ -221,18 +221,6 @@ class AuroLibraryRuntimeUtils {
221
221
  // See LICENSE in the project root for license information.
222
222
 
223
223
 
224
- /**
225
- * Serializes a multi-select value array back into the String `value` property.
226
- * An empty (or missing) array collapses to `undefined` so an emptied selection
227
- * clears `value` rather than reflecting a `"[]"` attribute.
228
- * @private
229
- * @param {Array<string>|undefined} values - The selected values.
230
- * @returns {string|undefined} JSON string of the values, or undefined when empty.
231
- */
232
- function serializeMultiSelectValue(values) {
233
- return values && values.length > 0 ? JSON.stringify(values) : undefined;
234
- }
235
-
236
224
  /**
237
225
  * Validates if an option can be interacted with.
238
226
  * @private
@@ -245,20 +233,6 @@ function isOptionInteractive(option) {
245
233
  !option.hasAttribute('static');
246
234
  }
247
235
 
248
- /**
249
- * Validates if an option may be selected by matching a programmatic value.
250
- * Unlike `isOptionInteractive`, `hidden` is allowed: the combobox toggles
251
- * `hidden` as its type-ahead filter, so a filtered-out option is still a
252
- * valid programmatic selection. Only disabled and static options — which are
253
- * never selectable — are rejected.
254
- * @param {HTMLElement} option - The option to check.
255
- * @returns {boolean} True if option can be selected by value.
256
- */
257
- function isSelectableByValue(option) {
258
- return !option.hasAttribute('disabled') &&
259
- !option.hasAttribute('static');
260
- }
261
-
262
236
  /**
263
237
  * Helper method to dispatch custom events.
264
238
  * @param {HTMLElement} element - Element to dispatch event from.
@@ -441,7 +415,6 @@ class AuroMenu extends AuroElement {
441
415
 
442
416
  /**
443
417
  * Specifies the current active menuOption.
444
- * @readonly
445
418
  */
446
419
  optionActive: {
447
420
  type: Object,
@@ -449,18 +422,15 @@ class AuroMenu extends AuroElement {
449
422
  },
450
423
 
451
424
  /**
452
- * The currently selected menu option(s). In single-select mode this is a single `HTMLElement` (or `undefined` when nothing is selected). In multi-select mode this is an array of `HTMLElement`s.
453
- * @readonly
425
+ * An array of currently selected menu options, type `HTMLElement` by default. In multi-select mode, `optionSelected` is an array of HTML elements.
454
426
  */
455
427
  optionSelected: {
456
428
  // Allow HTMLElement, HTMLElement[] arrays and undefined
457
- type: Object,
458
- attribute: false
429
+ type: Object
459
430
  },
460
431
 
461
432
  /**
462
433
  * The value of the selected option. In multi-select mode, this is a JSON stringified array of selected option values.
463
- * Options marked `disabled` or `static` are not selectable by value; `hidden` options remain selectable. In single-select mode, if the value matches a non-selectable option the selection is cleared (`optionSelected` becomes `undefined`) and `auroMenu-selectValueFailure` is dispatched. In multi-select mode, non-selectable entries are dropped from the value and the remaining selectable entries are selected; `auroMenu-selectValueFailure` is dispatched only when none of the entries match a selectable option.
464
434
  */
465
435
  value: {
466
436
  type: String,
@@ -584,7 +554,7 @@ class AuroMenu extends AuroElement {
584
554
  }
585
555
 
586
556
  /**
587
- * Selects options by value. Options marked `disabled` or `static` are not selectable; `hidden` options remain selectable. In single-select mode, if the value matches a non-selectable option the selection is cleared and `auroMenu-selectValueFailure` is dispatched. In multi-select mode, non-selectable entries are dropped and the remaining selectable entries are selected; `auroMenu-selectValueFailure` is dispatched only when none of the entries match a selectable option. Passing `undefined`, `null`, an empty string, or an empty array clears the selection without dispatching a failure.
557
+ * Selects options by value.
588
558
  * @param {string|string[]|undefined|null} value - The value(s) to select.
589
559
  * @public
590
560
  */
@@ -669,11 +639,6 @@ class AuroMenu extends AuroElement {
669
639
  this.initItems();
670
640
  }
671
641
 
672
- // Set when reconciliation reassigns `value` below. That reassignment schedules a
673
- // second updated() cycle, so the `event`-attribute dispatch is deferred to that
674
- // cycle to avoid firing option custom events twice on the same selection.
675
- let valueReconciled = false;
676
-
677
642
  // Handle null/undefined/empty case — empty/whitespace strings clear selection
678
643
  // consistently with selectByValue(''), and avoid downstream `.includes('')` matches.
679
644
  if (this.value === undefined || this.value === null || (typeof this.value === 'string' && this.value.trim() === '')) {
@@ -686,31 +651,11 @@ class AuroMenu extends AuroElement {
686
651
  // Defensive default: `formattedValue` can be undefined for unexpected value types,
687
652
  // and calling `.includes` on undefined would throw during reconciliation.
688
653
  const valueArray = this.formattedValue || [];
689
- const matchingOptions = this.items ? this.items.filter((item) => isSelectableByValue(item) && valueArray.includes(item.value)) : [];
654
+ const matchingOptions = this.items ? this.items.filter((item) => valueArray.includes(item.value)) : [];
690
655
  newSelected = matchingOptions.length > 0 ? matchingOptions : undefined;
691
-
692
- // Reconcile `value` with the selectable set. Drop only entries whose option is
693
- // loaded but non-selectable (disabled/static) — leaving them would desync `value`
694
- // from `optionSelected`, and the toggle handlers rebuild `value` from `formattedValue`,
695
- // so the rejected entry would resurface on the next select/deselect. Entries with no
696
- // matching item yet are preserved so async preselection still works once options render.
697
- const rejectedValues = this.items
698
- ? this.items.filter((item) => !isSelectableByValue(item) && valueArray.includes(item.value)).map((item) => item.value)
699
- : [];
700
- if (rejectedValues.length > 0) {
701
- const reconciled = valueArray.filter((val) => !rejectedValues.includes(val));
702
- this.value = serializeMultiSelectValue(reconciled);
703
- valueReconciled = true;
704
- }
705
656
  } else {
706
- // In single-select mode, this.value should be a string. Reject
707
- // disabled/static options so a programmatic value pointing at a
708
- // non-selectable option falls through to the no-match path below
709
- // (dispatching auroMenu-selectValueFailure) instead of pinning it.
710
- // `hidden` is intentionally NOT excluded: the combobox toggles
711
- // `hidden` as its type-ahead filter, so a filtered-out option is
712
- // still a valid programmatic selection.
713
- const matchingOption = this.items ? this.items.find((item) => isSelectableByValue(item) && item.value === this.value) : undefined;
657
+ // In single-select mode, this.value should be a string
658
+ const matchingOption = this.items ? this.items.find((item) => item.value === this.value) : undefined;
714
659
 
715
660
  if (matchingOption) {
716
661
  newSelected = matchingOption;
@@ -752,9 +697,8 @@ class AuroMenu extends AuroElement {
752
697
  ]
753
698
  ]));
754
699
 
755
- // Notify of changes. Skip when reconciliation just reassigned `value`: the
756
- // follow-on update cycle re-runs this branch and fires the events exactly once.
757
- if (this.optionSelected !== undefined && !valueReconciled) {
700
+ // Notify of changes
701
+ if (this.optionSelected !== undefined) {
758
702
  const selected = Array.isArray(this.optionSelected) ? this.optionSelected : [this.optionSelected];
759
703
  selected.forEach((opt) => {
760
704
  if (opt.hasAttribute('event')) {
@@ -967,7 +911,7 @@ class AuroMenu extends AuroElement {
967
911
  const currentSelected = this.optionSelected || [];
968
912
 
969
913
  if (!currentValue.includes(option.value)) {
970
- this.value = serializeMultiSelectValue([
914
+ this.value = JSON.stringify([
971
915
  ...currentValue,
972
916
  option.value
973
917
  ]);
@@ -993,9 +937,15 @@ class AuroMenu extends AuroElement {
993
937
  */
994
938
  handleDeselectState(option) {
995
939
  if (this.multiSelect) {
996
- // Remove this option from array; an empty result collapses `value` to undefined.
940
+ // Remove this option from array
997
941
  const newFormattedValue = (this.formattedValue || []).filter((val) => val !== option.value);
998
- this.value = serializeMultiSelectValue(newFormattedValue);
942
+
943
+ // If array is empty after removal, set back to undefined
944
+ if (newFormattedValue && newFormattedValue.length === 0) {
945
+ this.value = undefined;
946
+ } else {
947
+ this.value = JSON.stringify(newFormattedValue);
948
+ }
999
949
 
1000
950
  this.optionSelected = this.optionSelected.filter((val) => val !== option);
1001
951
  if (this.optionSelected.length === 0) {
@@ -1042,11 +992,6 @@ class AuroMenu extends AuroElement {
1042
992
  this.optionSelected = undefined;
1043
993
  this._index = -1;
1044
994
 
1045
- // Clear active option state so a follow-up open/navigation starts fresh
1046
- // rather than reusing a stale reference from before the reset.
1047
- this.items?.forEach((item) => item.classList.remove('active'));
1048
- this.optionActive = undefined;
1049
-
1050
995
  // Reset UI state
1051
996
  this.updateItemsState(new Map([
1052
997
  [
@@ -1509,7 +1454,6 @@ let menuOptionIdCounter = 0;
1509
1454
  * The `auro-menuoption` element provides users a way to define a menu option.
1510
1455
  * @customElement auro-menuoption
1511
1456
  *
1512
- * @attr {Boolean} static - When present, marks the option as non-interactive — it renders but is skipped during keyboard navigation and cannot be selected. Useful for section headers, informational rows inside a menu, or attaching event listeners.
1513
1457
  * @slot default - The default slot for the menu option text.
1514
1458
  *
1515
1459
  * @event auroMenuOption-mouseover - Notifies that this option has been hovered over.
@@ -1539,7 +1483,6 @@ class AuroMenuOption extends AuroElement {
1539
1483
  this.noCheckmark = false;
1540
1484
  this.disabled = false;
1541
1485
  this.noMatch = false;
1542
- this.persistent = false;
1543
1486
 
1544
1487
  /**
1545
1488
  * @private
@@ -1561,12 +1504,6 @@ class AuroMenuOption extends AuroElement {
1561
1504
  type: Boolean,
1562
1505
  reflect: true
1563
1506
  },
1564
-
1565
- /**
1566
- * **Deprecated.** Use the `value` attribute on `auro-menu` to set the selected option when the menu renders (or call `menu.selectByValue(value)` programmatically). Support for the child-level `selected` attribute will be removed in a future major release.
1567
- *
1568
- * @deprecated Use the `value` attribute on `auro-menu` instead.
1569
- */
1570
1507
  selected: {
1571
1508
  type: Boolean,
1572
1509
  reflect: true
@@ -1601,12 +1538,6 @@ class AuroMenuOption extends AuroElement {
1601
1538
  reflect: true,
1602
1539
  attribute: 'nomatch'
1603
1540
  },
1604
-
1605
- /** When true, this option is excluded from `matchWord` DOM rewriting — useful for utility rows (e.g., "Add new…") that must render identically regardless of the current filter. */
1606
- persistent: {
1607
- type: Boolean,
1608
- reflect: true
1609
- },
1610
1541
  };
1611
1542
  }
1612
1543
 
@@ -1781,103 +1712,6 @@ class AuroMenuOption extends AuroElement {
1781
1712
  }
1782
1713
  }
1783
1714
 
1784
- function auroMenuLoadingExample() {
1785
- document.querySelector("#loadingExampleToggleButton").addEventListener("click", () => {
1786
- document.querySelectorAll("#loadingExampleTable auro-menu").forEach(menu => menu.toggleAttribute("loading"));
1787
- });
1788
- }
1789
-
1790
- function updateMatch() {
1791
- let matchWordMenu = document.querySelector('#matchWordMenu');
1792
-
1793
- matchWordMenu.matchWord = matchWordInput.value;
1794
- }
1795
-
1796
- function auroMenuMatchWordExample() {
1797
- let matchWordInput = document.querySelector('#matchWordInput');
1798
-
1799
- matchWordInput.addEventListener('keyup', updateMatch);
1800
- }
1801
-
1802
- function auroMenuResetExample() {
1803
- const resetExampleBtnElem = document.querySelector('#resetExampleBtn');
1804
- const resetExampleElem = document.querySelector('#resetExample');
1805
-
1806
- if (resetExampleElem && resetExampleBtnElem) {
1807
- resetExampleBtnElem.addEventListener('click', () => {
1808
- resetExampleElem.reset();
1809
- });
1810
- }
1811
- }
1812
-
1813
- function auroMenuEventAttributeExample() {
1814
- const menu = document.querySelector('#eventAttrExample');
1815
- const output = document.querySelector('#eventAttrOutput');
1816
-
1817
- menu.addEventListener('auroMenu-customEventFired', (evt) => {
1818
- const eventName = evt.detail.option.getAttribute('event');
1819
- output.textContent = `${eventName} (from "${evt.detail.option.textContent}")`;
1820
- });
1821
- }
1822
-
1823
- function auroMenuEventsExample() {
1824
- const menu = document.querySelector('#eventsExampleMenu');
1825
- const log = document.querySelector('#eventsExampleLog');
1826
- const resetBtn = document.querySelector('#eventsExampleReset');
1827
- const badValueBtn = document.querySelector('#eventsExampleBadValue');
1828
- const clearBtn = document.querySelector('#eventsExampleClearLog');
1829
-
1830
- let entries = [];
1831
- const write = (name, detail) => {
1832
- const label = detail && detail.tagName ? detail.tagName.toLowerCase() : JSON.stringify(detail);
1833
- entries.unshift(`${name}: ${label}`);
1834
- log.textContent = entries.slice(0, 10).join('\n');
1835
- };
1836
-
1837
- menu.addEventListener('auroMenu-selectedOption', () => {
1838
- write('auroMenu-selectedOption', menu.optionSelected);
1839
- });
1840
- menu.addEventListener('auroMenu-activatedOption', (evt) => {
1841
- write('auroMenu-activatedOption', evt.detail);
1842
- });
1843
- menu.addEventListener('auroMenu-optionsChange', (evt) => {
1844
- write('auroMenu-optionsChange', { count: (evt.detail.options || []).length });
1845
- });
1846
- menu.addEventListener('auroMenu-selectValueReset', () => {
1847
- write('auroMenu-selectValueReset', { value: menu.value });
1848
- });
1849
- menu.addEventListener('auroMenu-selectValueFailure', (evt) => {
1850
- write('auroMenu-selectValueFailure', evt.detail);
1851
- });
1852
-
1853
- resetBtn.addEventListener('click', () => menu.reset());
1854
- badValueBtn.addEventListener('click', () => menu.selectByValue('nonexistent'));
1855
- clearBtn.addEventListener('click', () => {
1856
- entries = [];
1857
- log.textContent = '(interact with the menu above)';
1858
- });
1859
- }
1860
-
1861
- function auroMenuPersistentExample() {
1862
- const input = document.querySelector('#persistentExampleInput');
1863
- const menu = document.querySelector('#persistentExampleMenu');
1864
-
1865
- input.addEventListener('keyup', () => {
1866
- menu.matchWord = input.value;
1867
- });
1868
- }
1869
-
1870
- function auroMenuSelectByValueExample() {
1871
- const menu = document.querySelector('#selectByValueMenu');
1872
- const stopsBtn = document.querySelector('#selectByValueStops');
1873
- const durationBtn = document.querySelector('#selectByValueDuration');
1874
- const clearBtn = document.querySelector('#selectByValueClear');
1875
-
1876
- stopsBtn.addEventListener('click', () => menu.selectByValue('stops'));
1877
- durationBtn.addEventListener('click', () => menu.selectByValue('duration'));
1878
- clearBtn.addEventListener('click', () => menu.selectByValue(undefined));
1879
- }
1880
-
1881
1715
  /* eslint-disable jsdoc/require-jsdoc, no-magic-numbers, no-param-reassign */
1882
1716
 
1883
1717
 
@@ -1886,36 +1720,7 @@ AuroMenuOption.register();
1886
1720
  AuroMenu.register('custom-menu');
1887
1721
  AuroMenuOption.register('custom-menuoption');
1888
1722
 
1889
- const examples = [
1890
- auroMenuLoadingExample,
1891
- auroMenuMatchWordExample,
1892
- auroMenuResetExample,
1893
- auroMenuEventAttributeExample,
1894
- auroMenuEventsExample,
1895
- auroMenuPersistentExample,
1896
- auroMenuSelectByValueExample
1897
- ];
1898
-
1899
- function initExamples(pending, initCount) {
1900
- pending = pending || examples.slice();
1901
- initCount = initCount || 0;
1902
- const stillPending = [];
1903
-
1904
- for (const fn of pending) {
1905
- try {
1906
- fn();
1907
- } catch {
1908
- stillPending.push(fn);
1909
- }
1910
- }
1911
-
1912
- if (stillPending.length && initCount <= 20) {
1913
- setTimeout(() => {
1914
- initExamples(stillPending, initCount + 1);
1915
- }, 100);
1916
- }
1723
+ function initExamples(initCount) {
1917
1724
  }
1918
1725
 
1919
- initExamples();
1920
-
1921
1726
  export { initExamples };
@@ -7,90 +7,11 @@
7
7
  <auro-header level="2" id="keyEvents">Key Events</auro-header>
8
8
  <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../docs/partials/keyEvents.md) -->
9
9
  <!-- The below content is automatically added from ./../docs/partials/keyEvents.md -->
10
+ <!-- auro-menu does not implement direct keyboard event handling. -->
11
+ <!-- Keyboard navigation is delegated to parent components (auro-select, auro-combobox) via their keyboard strategy files. -->
10
12
  <div class="note">
11
- <p><strong>Note:</strong> <code>&lt;auro-menu&gt;</code> registers a <code>keydown</code> listener on its host. By default the menu is not focusable no <code>tabindex</code> on the host, and menuoptions are <code>tabindex="-1"</code> so the listener does not fire on its own. It fires whenever a <code>keydown</code> reaches the host, which happens in any of the following ways:</p>
12
- <ul>
13
- <li>A wrapping component focuses something inside the menu subtree and the event bubbles up to the host. In the standard integrations (<code>&lt;auro-select&gt;</code>, <code>&lt;auro-combobox&gt;</code>), keyboard behavior is primarily parent-managed rather than bubble-driven: focus stays on the parent's trigger/input, and the parent captures keys there and drives the menu directly via its public <code>navigateOptions()</code> / <code>updateActiveOption()</code> methods and the menu's internal <code>makeSelection()</code> routine (<code>@private</code>, not part of the public menu API).</li>
14
- <li>A wrapping component dispatches or reroutes a <code>KeyboardEvent</code> at the menu programmatically (for example, <code>menu.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }))</code>).</li>
15
- <li>The author makes the host focusable (for example, by adding <code>tabindex="0"</code> to the <code>&lt;auro-menu&gt;</code> element) and focuses it. The host already has <code>role="listbox"</code>, so this yields a bare, focusable listbox that responds to the keys in the table below. For full combobox semantics use <code>&lt;auro-select&gt;</code> or <code>&lt;auro-combobox&gt;</code>, or replicate their <code>aria-activedescendant</code> wiring from a separate trigger.</li>
16
- </ul>
17
- <p>Keys not listed below (<code>Home</code>, <code>End</code>, <code>Escape</code>, type-ahead) are handled by the parent — see the <code>&lt;auro-select&gt;</code> and <code>&lt;auro-combobox&gt;</code> keyboard behavior docs. An "interactive option" is one that is not <code>disabled</code>, <code>hidden</code>, or <code>static</code> — these are always skipped during navigation and selection, along with <code>&lt;hr&gt;</code> dividers.</p>
18
- </div>
19
- <table>
20
- <thead>
21
- <tr>
22
- <th>Key</th>
23
- <th>Current State</th>
24
- <th>Behavior</th>
25
- </tr>
26
- </thead>
27
- <tbody>
28
- <tr>
29
- <td rowspan="5">ArrowDown</td>
30
- <td>No option is active</td>
31
- <td>The first interactive option becomes active.</td>
32
- </tr>
33
- <tr>
34
- <td>An option is active, followed by interactive options</td>
35
- <td>Advances the active option to the next interactive option.</td>
36
- </tr>
37
- <tr>
38
- <td>The last interactive option is active</td>
39
- <td>Wraps to the first interactive option.</td>
40
- </tr>
41
- <tr>
42
- <td>Menu has no options or all options are non-interactive</td>
43
- <td>No option becomes active; no <code>auroMenu-activatedOption</code> event fires.</td>
44
- </tr>
45
- <tr>
46
- <td>Menu is in <code>loading</code> state</td>
47
- <td>The rendered loading placeholder is inert (marked <code>disabled</code>) and is not part of <code>items</code>, so it cannot become active.</td>
48
- </tr>
49
- <tr>
50
- <td rowspan="4">ArrowUp</td>
51
- <td>No option is active</td>
52
- <td>The last interactive option becomes active.</td>
53
- </tr>
54
- <tr>
55
- <td>An option is active, preceded by interactive options</td>
56
- <td>Advances the active option to the previous interactive option.</td>
57
- </tr>
58
- <tr>
59
- <td>The first interactive option is active</td>
60
- <td>Wraps to the last interactive option.</td>
61
- </tr>
62
- <tr>
63
- <td>Menu has no options or all options are non-interactive</td>
64
- <td>No option becomes active.</td>
65
- </tr>
66
- <tr>
67
- <td rowspan="4">Enter</td>
68
- <td>Single-select, active option is not currently selected</td>
69
- <td>The active option is selected. Any previous selection is cleared. <code>auroMenu-selectedOption</code> fires.</td>
70
- </tr>
71
- <tr>
72
- <td>Single-select, active option is already selected</td>
73
- <td>No selection change, but <code>auroMenu-selectedOption</code> still fires so the parent can react (for example, close the dropdown).</td>
74
- </tr>
75
- <tr>
76
- <td>Multi-select</td>
77
- <td>The active option toggles selected. Other selections are preserved.</td>
78
- </tr>
79
- <tr>
80
- <td>Active option is disabled, hidden, or static</td>
81
- <td>Selection is a no-op.</td>
82
- </tr>
83
- <tr>
84
- <td rowspan="2">Tab</td>
85
- <td>An option is active</td>
86
- <td>The active option is selected (same behavior as <code>Enter</code>).<div class="note"><strong>Note:</strong> <code>Tab</code> does not <code>preventDefault</code>, so focus continues to the next element in the tabindex sequence after selection.</div></td>
87
- </tr>
88
- <tr>
89
- <td>No option is active</td>
90
- <td>Focus moves to the next tabbable element; no selection change.</td>
91
- </tr>
92
- </tbody>
93
- </table>
13
+ <p><strong>Note:</strong> The <code>&lt;auro-menu&gt;</code> component does not handle keyboard events directly. Keyboard navigation is managed by the parent component (e.g., <code>&lt;auro-select&gt;</code>, <code>&lt;auro-combobox&gt;</code>) through their keyboard strategy classes, which call menu methods such as <code>navigateOptions()</code>, <code>makeSelection()</code>, and <code>updateActiveOption()</code>.</p>
14
+ </div>
94
15
  <!-- AURO-GENERATED-CONTENT:END -->
95
16
  </div>
96
17
  </div>