@aurodesignsystem/auro-formkit 2.2.1-beta.2 → 2.2.1

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 (44) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +67 -67
  3. package/components/checkbox/README.md +1 -1
  4. package/components/checkbox/demo/readme.md +1 -1
  5. package/components/combobox/README.md +1 -1
  6. package/components/combobox/demo/api.min.js +352 -94
  7. package/components/combobox/demo/index.min.js +352 -94
  8. package/components/combobox/demo/readme.md +1 -1
  9. package/components/combobox/dist/index.js +352 -94
  10. package/components/combobox/dist/registered.js +352 -94
  11. package/components/counter/README.md +1 -1
  12. package/components/counter/demo/api.min.js +353 -95
  13. package/components/counter/demo/index.min.js +353 -95
  14. package/components/counter/demo/readme.md +1 -1
  15. package/components/counter/dist/index.js +353 -95
  16. package/components/counter/dist/registered.js +353 -95
  17. package/components/datepicker/README.md +1 -1
  18. package/components/datepicker/demo/api.min.js +466 -232
  19. package/components/datepicker/demo/index.min.js +466 -232
  20. package/components/datepicker/demo/readme.md +1 -1
  21. package/components/datepicker/dist/index.js +354 -114
  22. package/components/datepicker/dist/registered.js +354 -114
  23. package/components/dropdown/README.md +1 -1
  24. package/components/dropdown/demo/api.min.js +352 -94
  25. package/components/dropdown/demo/index.min.js +352 -94
  26. package/components/dropdown/demo/readme.md +1 -1
  27. package/components/dropdown/dist/auro-dropdown.d.ts +18 -0
  28. package/components/dropdown/dist/index.js +352 -94
  29. package/components/dropdown/dist/registered.js +352 -94
  30. package/components/form/README.md +1 -1
  31. package/components/form/demo/readme.md +1 -1
  32. package/components/input/README.md +1 -1
  33. package/components/input/demo/readme.md +1 -1
  34. package/components/menu/README.md +1 -1
  35. package/components/menu/demo/readme.md +1 -1
  36. package/components/radio/README.md +1 -1
  37. package/components/radio/demo/readme.md +1 -1
  38. package/components/select/README.md +1 -1
  39. package/components/select/demo/api.min.js +352 -94
  40. package/components/select/demo/index.min.js +352 -94
  41. package/components/select/demo/readme.md +1 -1
  42. package/components/select/dist/index.js +352 -94
  43. package/components/select/dist/registered.js +352 -94
  44. package/package.json +2 -2
@@ -2155,17 +2155,70 @@ const computePosition = (reference, floating, options) => {
2155
2155
  /* eslint-disable line-comment-position, no-inline-comments */
2156
2156
 
2157
2157
 
2158
+
2159
+ const MAX_CONFIGURATION_COUNT = 10;
2160
+
2158
2161
  class AuroFloatingUI {
2159
- constructor() {
2162
+
2163
+ /**
2164
+ * @private
2165
+ */
2166
+ static isMousePressed = false;
2167
+
2168
+ /**
2169
+ * @private
2170
+ */
2171
+ static isMousePressHandlerInitialized = false;
2172
+
2173
+ /**
2174
+ * @private
2175
+ */
2176
+ static setupMousePressChecker() {
2177
+ if (!AuroFloatingUI.isMousePressHandlerInitialized && window && window.addEventListener) {
2178
+ AuroFloatingUI.isMousePressHandlerInitialized = true;
2179
+
2180
+ const mouseEventGlobalHandler = (event) => {
2181
+ AuroFloatingUI.isMousePressed = event.type === 'mousedown';
2182
+ };
2183
+
2184
+ window.addEventListener('mousedown', mouseEventGlobalHandler);
2185
+ window.addEventListener('mouseup', mouseEventGlobalHandler);
2186
+ }
2187
+ }
2188
+
2189
+ constructor(element, behavior) {
2190
+ this.element = element;
2191
+ this.behavior = behavior;
2192
+
2160
2193
  // Store event listener references for cleanup
2161
2194
  this.focusHandler = null;
2162
2195
  this.clickHandler = null;
2163
2196
  this.keyDownHandler = null;
2164
-
2197
+
2198
+ /**
2199
+ * @private
2200
+ */
2201
+ this.configureTrial = 0;
2202
+
2165
2203
  /**
2166
2204
  * @private
2167
2205
  */
2168
2206
  this.eventPrefix = undefined;
2207
+
2208
+ /**
2209
+ * @private
2210
+ */
2211
+ this.id = undefined;
2212
+
2213
+ /**
2214
+ * @private
2215
+ */
2216
+ this.showing = false;
2217
+
2218
+ /**
2219
+ * @private
2220
+ */
2221
+ this.strategy = undefined;
2169
2222
  }
2170
2223
 
2171
2224
  /**
@@ -2193,29 +2246,48 @@ class AuroFloatingUI {
2193
2246
  * @private
2194
2247
  * Determines the positioning strategy based on the current viewport size and mobile breakpoint.
2195
2248
  *
2196
- * This method checks if the current viewport width is less than or equal to the specified mobile fullscreen breakpoint
2249
+ * This method checks if the current viewport width is less than or equal to the specified mobile fullscreen breakpoint
2197
2250
  * defined in the bib element. If it is, the strategy is set to 'fullscreen'; otherwise, it defaults to 'floating'.
2198
2251
  *
2199
- * @returns {String} The positioning strategy, either 'fullscreen' or 'floating'.
2252
+ * @returns {String} The positioning strategy, one of 'fullscreen', 'floating', 'cover'.
2200
2253
  */
2201
2254
  getPositioningStrategy() {
2202
- let strategy = 'floating';
2203
- if (this.element.bib.mobileFullscreenBreakpoint) {
2204
- const smallerThanBreakpoint = window.matchMedia(`(max-width: ${this.element.bib.mobileFullscreenBreakpoint})`).matches;
2205
- if (smallerThanBreakpoint) {
2206
- strategy = 'fullscreen';
2207
- }
2255
+ const breakpoint = this.element.bib.mobileFullscreenBreakpoint || this.element.floaterConfig?.fullscreenBreakpoint;
2256
+ switch (this.behavior) {
2257
+ case "tooltip":
2258
+ return "floating";
2259
+ case "dialog":
2260
+ case "drawer":
2261
+ if (breakpoint) {
2262
+ const smallerThanBreakpoint = window.matchMedia(`(max-width: ${breakpoint})`).matches;
2263
+
2264
+ this.element.expanded = smallerThanBreakpoint;
2265
+ }
2266
+ if (this.element.nested) {
2267
+ return "cover";
2268
+ }
2269
+ return 'fullscreen';
2270
+ case "dropdown":
2271
+ case undefined:
2272
+ case null:
2273
+ if (breakpoint) {
2274
+ const smallerThanBreakpoint = window.matchMedia(`(max-width: ${breakpoint})`).matches;
2275
+ if (smallerThanBreakpoint) {
2276
+ return 'fullscreen';
2277
+ }
2278
+ }
2279
+ return "floating";
2280
+ default:
2281
+ return this.behavior;
2208
2282
  }
2209
-
2210
- return strategy;
2211
2283
  }
2212
2284
 
2213
2285
  /**
2214
2286
  * @private
2215
2287
  * Positions the bib element based on the current configuration and positioning strategy.
2216
2288
  *
2217
- * This method determines the appropriate positioning strategy (fullscreen or not) and configures the bib accordingly.
2218
- * It also sets up middleware for the floater configuration, computes the position of the bib relative to the trigger element,
2289
+ * This method determines the appropriate positioning strategy (fullscreen or not) and configures the bib accordingly.
2290
+ * It also sets up middleware for the floater configuration, computes the position of the bib relative to the trigger element,
2219
2291
  * and applies the calculated position to the bib's style.
2220
2292
  */
2221
2293
  position() {
@@ -2226,21 +2298,33 @@ class AuroFloatingUI {
2226
2298
  this.mirrorSize();
2227
2299
  // Define the middlware for the floater configuration
2228
2300
  const middleware = [
2229
- offset(this.element.floaterConfig.offset || 0),
2230
- ...(this.element.floaterConfig.flip ? [flip()] : []), // Add flip middleware if flip is enabled
2231
- ...(this.element.floaterConfig.autoPlacement ? [autoPlacement()] : []), // Add autoPlacement middleware if autoPlacement is enabled
2301
+ offset(this.element.floaterConfig?.offset || 0),
2302
+ ...this.element.floaterConfig?.flip ? [flip()] : [], // Add flip middleware if flip is enabled.
2303
+ ...this.element.floaterConfig?.autoPlacement ? [autoPlacement()] : [], // Add autoPlacement middleware if autoPlacement is enabled.
2232
2304
  ];
2233
2305
 
2234
2306
  // Compute the position of the bib
2235
2307
  computePosition(this.element.trigger, this.element.bib, {
2236
- placement: this.element.floaterConfig.placement || 'bottom',
2308
+ placement: this.element.floaterConfig?.placement,
2237
2309
  middleware: middleware || []
2238
- }).then(({x, y}) => { // eslint-disable-line id-length
2310
+ }).then(({ x, y }) => { // eslint-disable-line id-length
2239
2311
  Object.assign(this.element.bib.style, {
2240
2312
  left: `${x}px`,
2241
2313
  top: `${y}px`,
2242
2314
  });
2243
2315
  });
2316
+ } else if (strategy === 'cover') {
2317
+ // Compute the position of the bib
2318
+ computePosition(this.element.parentNode, this.element.bib, {
2319
+ placement: 'bottom-start'
2320
+ }).then(({ x, y }) => { // eslint-disable-line id-length
2321
+ Object.assign(this.element.bib.style, {
2322
+ left: `${x}px`,
2323
+ top: `${y - this.element.parentNode.offsetHeight}px`,
2324
+ width: `${this.element.parentNode.offsetWidth}px`,
2325
+ height: `${this.element.parentNode.offsetHeight}px`
2326
+ });
2327
+ });
2244
2328
  }
2245
2329
  }
2246
2330
 
@@ -2269,34 +2353,48 @@ class AuroFloatingUI {
2269
2353
  *
2270
2354
  * @param {string} strategy - The positioning strategy ('fullscreen' or 'floating').
2271
2355
  */
2272
- configureBibStrategy(strategy) {
2273
- const prevStrategy = this.element.isBibFullscreen ? 'fullscreen' : 'floating';
2274
- if (strategy === 'fullscreen') {
2356
+ configureBibStrategy(value) {
2357
+ if (value === 'fullscreen') {
2275
2358
  this.element.isBibFullscreen = true;
2276
2359
  // reset the prev position
2360
+ this.element.bib.setAttribute('isfullscreen', "");
2361
+ this.element.bib.style.position = 'fixed';
2277
2362
  this.element.bib.style.top = "0px";
2278
2363
  this.element.bib.style.left = "0px";
2364
+ this.element.bib.style.width = '';
2365
+ this.element.bib.style.height = '';
2279
2366
 
2280
2367
  // reset the size that was mirroring `size` css-part
2281
2368
  const bibContent = this.element.bib.shadowRoot.querySelector(".container");
2282
- bibContent.style.width = '';
2283
- bibContent.style.height = '';
2284
- bibContent.style.maxWidth = '';
2285
- bibContent.style.maxHeight = `${window.visualViewport.height}px`;
2369
+ if (bibContent) {
2370
+ bibContent.style.width = '';
2371
+ bibContent.style.height = '';
2372
+ bibContent.style.maxWidth = '';
2373
+ bibContent.style.maxHeight = `${window.visualViewport.height}px`;
2374
+ this.configureTrial = 0;
2375
+ } else if (this.configureTrial < MAX_CONFIGURATION_COUNT) {
2376
+ this.configureTrial += 1;
2377
+
2378
+ setTimeout(() => {
2379
+ this.configureBibStrategy(value);
2380
+ }, 0);
2381
+ }
2286
2382
 
2287
2383
  if (this.element.isPopoverVisible) {
2288
2384
  this.lockScroll(true);
2289
2385
  }
2290
2386
  } else {
2387
+ this.element.bib.style.position = '';
2388
+ this.element.bib.removeAttribute('isfullscreen');
2291
2389
  this.element.isBibFullscreen = false;
2292
-
2293
- this.lockScroll(false);
2294
2390
  }
2295
2391
 
2296
- if (prevStrategy !== strategy) {
2392
+ const isChanged = this.strategy && this.strategy !== value;
2393
+ this.strategy = value;
2394
+ if (isChanged) {
2297
2395
  const event = new CustomEvent(this.eventPrefix ? `${this.eventPrefix}-strategy-change` : 'strategy-change', {
2298
2396
  detail: {
2299
- strategy,
2397
+ value,
2300
2398
  },
2301
2399
  composed: true
2302
2400
  });
@@ -2307,18 +2405,6 @@ class AuroFloatingUI {
2307
2405
 
2308
2406
  updateState() {
2309
2407
  const isVisible = this.element.isPopoverVisible;
2310
-
2311
- // Refactor this to apply attribute to correct focusable element
2312
- // Reference Issue: https://github.com/AlaskaAirlines/auro-library/issues/105
2313
- //
2314
- // this.element.trigger.setAttribute('aria-expanded', isVisible);
2315
-
2316
- if (isVisible) {
2317
- this.element.bib.setAttribute('data-show', true);
2318
- } else {
2319
- this.element.bib.removeAttribute('data-show');
2320
- }
2321
-
2322
2408
  if (!isVisible) {
2323
2409
  this.cleanupHideHandlers();
2324
2410
  try {
@@ -2329,16 +2415,32 @@ class AuroFloatingUI {
2329
2415
  }
2330
2416
  }
2331
2417
 
2418
+ /**
2419
+ * @private
2420
+ * getting called on 'blur' in trigger or `focusin` in document
2421
+ *
2422
+ * Hides the bib if focus moves outside of the trigger or bib, unless a 'noHideOnThisFocusLoss' flag is set.
2423
+ * This method checks if the currently active element is still within the trigger or bib.
2424
+ * If not, and if the bib isn't in fullscreen mode with focus lost, it hides the bib.
2425
+ */
2332
2426
  handleFocusLoss() {
2333
- if (this.element.noHideOnThisFocusLoss ||
2334
- this.element.hasAttribute('noHideOnThisFocusLoss')) {
2427
+ // if mouse is being pressed, skip and let click event to handle the action
2428
+ if (AuroFloatingUI.isMousePressed) {
2429
+ return;
2430
+ }
2431
+
2432
+ if (this.element.noHideOnThisFocusLoss ||
2433
+ this.element.hasAttribute('noHideOnThisFocusLoss')) {
2335
2434
  return;
2336
2435
  }
2337
2436
 
2338
- const {activeElement} = document;
2339
- if (activeElement === document.querySelector('body') ||
2340
- this.element.contains(activeElement) ||
2341
- this.element.bibContent?.contains(activeElement)) {
2437
+ const { activeElement } = document;
2438
+ // if focus is still inside of trigger or bib, do not close
2439
+ if (this.element.contains(activeElement) || this.element.bib?.contains(activeElement)) {
2440
+ return;
2441
+ }
2442
+ // if fullscreen bib is still open and the focus is missing, do not close
2443
+ if (this.element.bib.hasAttribute('isfullscreen') && activeElement === document.body) {
2342
2444
  return;
2343
2445
  }
2344
2446
 
@@ -2346,31 +2448,66 @@ class AuroFloatingUI {
2346
2448
  }
2347
2449
 
2348
2450
  setupHideHandlers() {
2451
+ this.preventFocusLoseOnBibClick = (event) => {
2452
+ event.preventDefault();
2453
+ event.stopPropagation();
2454
+ };
2455
+ this.element.bib.addEventListener('mousedown', this.preventFocusLoseOnBibClick);
2456
+
2349
2457
  // Define handlers & store references
2350
2458
  this.focusHandler = () => this.handleFocusLoss();
2351
2459
 
2352
2460
  this.clickHandler = (evt) => {
2353
- if (!evt.composedPath().includes(this.element.trigger) &&
2354
- !evt.composedPath().includes(this.element.bibContent)) {
2355
- this.hideBib();
2461
+ if ((!evt.composedPath().includes(this.element.trigger) &&
2462
+ !evt.composedPath().includes(this.element.bib)) ||
2463
+ (this.element.bib.backdrop && evt.composedPath().includes(this.element.bib.backdrop))) {
2464
+ const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
2465
+
2466
+ if (existedVisibleFloatingUI && existedVisibleFloatingUI.element.isPopoverVisible) {
2467
+ // if something else is open, close that
2468
+ existedVisibleFloatingUI.hideBib();
2469
+ document.expandedAuroFormkitDropdown = null;
2470
+ document.expandedAuroFloater = this;
2471
+ } else {
2472
+ this.hideBib();
2473
+ }
2356
2474
  }
2357
2475
  };
2358
2476
 
2359
2477
  // ESC key handler
2360
2478
  this.keyDownHandler = (evt) => {
2361
2479
  if (evt.key === 'Escape' && this.element.isPopoverVisible) {
2480
+ const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
2481
+ if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this && existedVisibleFloatingUI.element.isPopoverVisible) {
2482
+ // if something else is open, let it handle itself
2483
+ return;
2484
+ }
2362
2485
  this.hideBib();
2363
2486
  }
2364
2487
  };
2365
2488
 
2366
- // Add event listeners using the stored references
2367
- document.addEventListener('focusin', this.focusHandler);
2368
- window.addEventListener('click', this.clickHandler);
2489
+ if (this.behavior !== 'drawer' && this.behavior !== 'dialog') {
2490
+ // Add event listeners using the stored references
2491
+ document.addEventListener('focusin', this.focusHandler);
2492
+ }
2493
+
2369
2494
  document.addEventListener('keydown', this.keyDownHandler);
2495
+
2496
+ // send this task to the end of queue to prevent conflicting
2497
+ // it conflicts if showBib gets call from a button that's not this.element.trigger
2498
+ setTimeout(() => {
2499
+ window.addEventListener('click', this.clickHandler);
2500
+ }, 0);
2370
2501
  }
2371
2502
 
2372
2503
  cleanupHideHandlers() {
2373
2504
  // Remove event listeners if they exist
2505
+
2506
+ if (this.preventFocusLoseOnBibClick) {
2507
+ this.element.bib.removeEventListener('mousedown', this.preventFocusLoseOnBibClick);
2508
+ delete this.preventFocusLoseOnBibClick;
2509
+ }
2510
+
2374
2511
  if (this.focusHandler) {
2375
2512
  document.removeEventListener('focusin', this.focusHandler);
2376
2513
  this.focusHandler = null;
@@ -2395,40 +2532,54 @@ class AuroFloatingUI {
2395
2532
 
2396
2533
  updateCurrentExpandedDropdown() {
2397
2534
  // Close any other dropdown that is already open
2398
- if (document.expandedAuroFormkitDropdown) {
2399
- document.expandedAuroFormkitDropdown.hide;
2535
+ const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
2536
+ if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
2537
+ existedVisibleFloatingUI.isPopoverVisible &&
2538
+ document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
2539
+ document.expandedAuroFloater.hideBib();
2400
2540
  }
2401
2541
 
2402
- document.expandedAuroFormkitDropdown = this;
2542
+ document.expandedAuroFloater = this;
2403
2543
  }
2404
2544
 
2405
2545
  showBib() {
2406
- if (!this.element.disabled && !this.element.isPopoverVisible) {
2546
+ if (!this.element.disabled && !this.showing) {
2407
2547
  this.updateCurrentExpandedDropdown();
2408
- this.element.isPopoverVisible = true;
2409
2548
  this.element.triggerChevron?.setAttribute('data-expanded', true);
2410
-
2411
- this.dispatchEventDropdownToggle();
2412
- this.position();
2413
-
2414
- // Clean up any existing handlers before setting up new ones
2415
- this.cleanupHideHandlers();
2416
- this.setupHideHandlers();
2549
+
2550
+ // prevent double showing: isPopovervisible gets first and showBib gets called later
2551
+ if (!this.showing) {
2552
+ if (!this.element.modal) {
2553
+ this.setupHideHandlers();
2554
+ }
2555
+ this.showing = true;
2556
+ this.element.isPopoverVisible = true;
2557
+ this.position();
2558
+ this.dispatchEventDropdownToggle();
2559
+ }
2417
2560
 
2418
2561
  // Setup auto update to handle resize and scroll
2419
- this.element.cleanup = autoUpdate(this.element.trigger, this.element.bib, () => {
2562
+ this.element.cleanup = autoUpdate(this.element.trigger || this.element.parentNode, this.element.bib, () => {
2420
2563
  this.position();
2421
2564
  });
2422
2565
  }
2423
2566
  }
2424
2567
 
2425
2568
  hideBib() {
2426
- if (this.element.isPopoverVisible && !this.element.disabled && !this.element.noToggle) {
2427
- this.element.isPopoverVisible = false;
2569
+ if (!this.element.disabled && !this.element.noToggle) {
2428
2570
  this.lockScroll(false);
2429
2571
  this.element.triggerChevron?.removeAttribute('data-expanded');
2430
- this.dispatchEventDropdownToggle();
2572
+
2573
+ if (this.element.isPopoverVisible) {
2574
+ this.element.isPopoverVisible = false;
2575
+ }
2576
+ if (this.showing) {
2577
+ this.cleanupHideHandlers();
2578
+ this.showing = false;
2579
+ this.dispatchEventDropdownToggle();
2580
+ }
2431
2581
  }
2582
+ document.expandedAuroFloater = null;
2432
2583
  }
2433
2584
 
2434
2585
  /**
@@ -2438,7 +2589,7 @@ class AuroFloatingUI {
2438
2589
  dispatchEventDropdownToggle() {
2439
2590
  const event = new CustomEvent(this.eventPrefix ? `${this.eventPrefix}-toggled` : 'toggled', {
2440
2591
  detail: {
2441
- expanded: this.element.isPopoverVisible,
2592
+ expanded: this.showing,
2442
2593
  },
2443
2594
  composed: true
2444
2595
  });
@@ -2486,15 +2637,18 @@ class AuroFloatingUI {
2486
2637
  break;
2487
2638
  case 'focus':
2488
2639
  if (this.element.focusShow) {
2640
+
2489
2641
  /*
2490
- This needs to better handle clicking that gives focus -
2491
- currently it shows and then immediately hides the bib
2642
+ This needs to better handle clicking that gives focus -
2643
+ currently it shows and then immediately hides the bib
2492
2644
  */
2493
2645
  this.showBib();
2494
2646
  }
2495
2647
  break;
2496
2648
  case 'blur':
2497
- this.handleFocusLoss();
2649
+ // send this task 100ms later queue to
2650
+ // wait a frame in case focus moves within the floating element/bib
2651
+ setTimeout(() => this.handleFocusLoss(), 0);
2498
2652
  break;
2499
2653
  case 'click':
2500
2654
  if (document.activeElement === document.body) {
@@ -2502,7 +2656,7 @@ class AuroFloatingUI {
2502
2656
  }
2503
2657
  this.handleClick();
2504
2658
  break;
2505
- // Do nothing
2659
+ // Do nothing
2506
2660
  }
2507
2661
  }
2508
2662
  }
@@ -2546,39 +2700,79 @@ class AuroFloatingUI {
2546
2700
  });
2547
2701
  }
2548
2702
 
2703
+ /**
2704
+ *
2705
+ * @param {*} eventPrefix
2706
+ */
2707
+ regenerateBibId() {
2708
+ this.id = this.element.getAttribute('id');
2709
+ if (!this.id) {
2710
+ this.id = window.crypto.randomUUID();
2711
+ this.element.setAttribute('id', this.id);
2712
+ }
2713
+
2714
+ this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
2715
+ }
2716
+
2549
2717
  configure(elem, eventPrefix) {
2718
+ AuroFloatingUI.setupMousePressChecker();
2719
+
2550
2720
  this.eventPrefix = eventPrefix;
2551
- this.element = elem;
2552
- this.element.trigger = this.element.shadowRoot.querySelector('#trigger');
2553
- this.element.bib = this.element.shadowRoot.querySelector('#bib');
2721
+ if (this.element !== elem) {
2722
+ this.element = elem;
2723
+ }
2724
+
2725
+ if (this.behavior !== this.element.behavior) {
2726
+ this.behavior = this.element.behavior;
2727
+ }
2728
+
2729
+ if (this.element.trigger) {
2730
+ this.disconnect();
2731
+ }
2732
+ this.element.trigger = this.element.triggerElement || this.element.shadowRoot.querySelector('#trigger') || this.element.trigger;
2733
+ this.element.bib = this.element.shadowRoot.querySelector('#bib') || this.element.bib;
2554
2734
  this.element.bibSizer = this.element.shadowRoot.querySelector('#bibSizer');
2555
2735
  this.element.triggerChevron = this.element.shadowRoot.querySelector('#showStateIcon');
2556
2736
 
2737
+
2738
+ if (this.element.floaterConfig) {
2739
+ this.element.hoverToggle = this.element.floaterConfig.hoverToggle;
2740
+ }
2741
+
2557
2742
  document.body.append(this.element.bib);
2558
2743
 
2744
+ this.regenerateBibId();
2559
2745
  this.handleTriggerTabIndex();
2560
2746
 
2561
2747
  this.handleEvent = this.handleEvent.bind(this);
2562
- this.element.trigger.addEventListener('keydown', this.handleEvent);
2563
- this.element.trigger.addEventListener('click', this.handleEvent);
2564
- this.element.trigger.addEventListener('mouseenter', this.handleEvent);
2565
- this.element.trigger.addEventListener('mouseleave', this.handleEvent);
2566
- this.element.trigger.addEventListener('focus', this.handleEvent);
2567
- this.element.trigger.addEventListener('blur', this.handleEvent);
2748
+ if (this.element.trigger) {
2749
+ this.element.trigger.addEventListener('keydown', this.handleEvent);
2750
+ this.element.trigger.addEventListener('click', this.handleEvent);
2751
+ this.element.trigger.addEventListener('mouseenter', this.handleEvent);
2752
+ this.element.trigger.addEventListener('mouseleave', this.handleEvent);
2753
+ this.element.trigger.addEventListener('focus', this.handleEvent);
2754
+ this.element.trigger.addEventListener('blur', this.handleEvent);
2755
+ }
2568
2756
  }
2569
2757
 
2570
2758
  disconnect() {
2571
2759
  this.cleanupHideHandlers();
2572
- this.element.cleanup?.();
2573
-
2574
- // Remove event & keyboard listeners
2575
- if (this.element?.trigger) {
2576
- this.element.trigger.removeEventListener('keydown', this.handleEvent);
2577
- this.element.trigger.removeEventListener('click', this.handleEvent);
2578
- this.element.trigger.removeEventListener('mouseenter', this.handleEvent);
2579
- this.element.trigger.removeEventListener('mouseleave', this.handleEvent);
2580
- this.element.trigger.removeEventListener('focus', this.handleEvent);
2581
- this.element.trigger.removeEventListener('blur', this.handleEvent);
2760
+ if (this.element) {
2761
+ this.element.cleanup?.();
2762
+
2763
+ if (this.element.bib) {
2764
+ this.element.shadowRoot.append(this.element.bib);
2765
+ }
2766
+
2767
+ // Remove event & keyboard listeners
2768
+ if (this.element?.trigger) {
2769
+ this.element.trigger.removeEventListener('keydown', this.handleEvent);
2770
+ this.element.trigger.removeEventListener('click', this.handleEvent);
2771
+ this.element.trigger.removeEventListener('mouseenter', this.handleEvent);
2772
+ this.element.trigger.removeEventListener('mouseleave', this.handleEvent);
2773
+ this.element.trigger.removeEventListener('focus', this.handleEvent);
2774
+ this.element.trigger.removeEventListener('blur', this.handleEvent);
2775
+ }
2582
2776
  }
2583
2777
  }
2584
2778
  }
@@ -3541,6 +3735,11 @@ class AuroDropdown extends r {
3541
3735
  * @private
3542
3736
  */
3543
3737
  this.helpTextTag = versioning.generateTag('auro-formkit-dropdown-helptext', helpTextVersion, AuroHelpText);
3738
+
3739
+ /**
3740
+ * @private
3741
+ */
3742
+ this.bindFocusEventToTrigger = this.bindFocusEventToTrigger.bind(this);
3544
3743
  }
3545
3744
 
3546
3745
  /**
@@ -3809,6 +4008,7 @@ class AuroDropdown extends r {
3809
4008
  disconnectedCallback() {
3810
4009
  super.disconnectedCallback();
3811
4010
  this.floater.disconnect();
4011
+ this.clearTriggerFocusEventBinding();
3812
4012
  }
3813
4013
 
3814
4014
  updated(changedProperties) {
@@ -3923,6 +4123,62 @@ class AuroDropdown extends r {
3923
4123
  return result;
3924
4124
  }
3925
4125
 
4126
+ /**
4127
+ * @private
4128
+ * Creates and dispatches a duplicate focus event on the trigger element.
4129
+ * @param {Event} event - The original focus event.
4130
+ */
4131
+ bindFocusEventToTrigger(event) {
4132
+ const dupEvent = new FocusEvent(event.type, {
4133
+ bubbles: false,
4134
+ cancelable: false,
4135
+ composed: true,
4136
+ });
4137
+ this.trigger.dispatchEvent(dupEvent);
4138
+ }
4139
+
4140
+ /**
4141
+ * @private
4142
+ * Sets up event listeners to deliver focus and blur events from nested Auro components within the trigger slot to trigger.
4143
+ * This ensures that focus/blur events originating from within these components are propagated to the trigger element itself.
4144
+ */
4145
+ setupTriggerFocusEventBinding() {
4146
+ if (!this.triggerContentSlot || this.triggerContentSlot.length === 0) {
4147
+ return;
4148
+ }
4149
+
4150
+ this.triggerContentSlot.forEach((node) => {
4151
+ if (node.querySelectorAll) {
4152
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
4153
+ auroElements.forEach((auroEl) => {
4154
+ auroEl.addEventListener('focus', this.bindFocusEventToTrigger);
4155
+ auroEl.addEventListener('blur', this.bindFocusEventToTrigger);
4156
+ });
4157
+ }
4158
+ });
4159
+ }
4160
+
4161
+ /**
4162
+ * Clears focus and blur event listeners from nested Auro components within the trigger slot.
4163
+ * @private
4164
+ * @returns {void}
4165
+ */
4166
+ clearTriggerFocusEventBinding() {
4167
+ if (!this.triggerContentSlot || this.triggerContentSlot.length === 0) {
4168
+ return;
4169
+ }
4170
+
4171
+ this.triggerContentSlot.forEach((node) => {
4172
+ if (node.querySelectorAll) {
4173
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
4174
+ auroElements.forEach((auroEl) => {
4175
+ auroEl.removeEventListener('focus', this.bindFocusEventToTrigger);
4176
+ auroEl.removeEventListener('blur', this.bindFocusEventToTrigger);
4177
+ });
4178
+ }
4179
+ });
4180
+ }
4181
+
3926
4182
  /**
3927
4183
  * Handles changes to the trigger content slot and updates related properties.
3928
4184
  *
@@ -3970,6 +4226,7 @@ class AuroDropdown extends r {
3970
4226
  }
3971
4227
 
3972
4228
  if (this.triggerContentSlot) {
4229
+ this.setupTriggerFocusEventBinding();
3973
4230
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
3974
4231
  if (slot.textContent.trim()) {
3975
4232
  return true;
@@ -4075,6 +4332,7 @@ class AuroDropdown extends r {
4075
4332
  <${this.dropdownBibTag}
4076
4333
  id="bib"
4077
4334
  role="tooltip"
4335
+ ?data-show="${this.isPopoverVisible}"
4078
4336
  ?isfullscreen="${this.isBibFullscreen}"
4079
4337
  ?common="${this.common}"
4080
4338
  ?rounded="${this.common || this.rounded}"