bits-ui 2.16.3 → 2.16.4

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.
@@ -183,6 +183,10 @@ export class MenuContentState {
183
183
  this.opts.onCloseAutoFocus.current?.(e);
184
184
  if (e.defaultPrevented || this.#isSub)
185
185
  return;
186
+ if (this.parentMenu.root.ignoreCloseAutoFocus) {
187
+ e.preventDefault();
188
+ return;
189
+ }
186
190
  if (this.parentMenu.triggerNode && isTabbable(this.parentMenu.triggerNode)) {
187
191
  e.preventDefault();
188
192
  this.parentMenu.triggerNode.focus();
@@ -306,7 +310,17 @@ export class MenuContentState {
306
310
  }
307
311
  if (e.target.closest(`#${triggerId}`)) {
308
312
  e.preventDefault();
313
+ return;
309
314
  }
315
+ /**
316
+ * when the menu closes due to an outside pointer interaction (for example,
317
+ * clicking another dropdown trigger), avoid focusing this menu's trigger
318
+ * to prevent stealing focus from the new interaction target.
319
+ */
320
+ this.parentMenu.root.ignoreCloseAutoFocus = true;
321
+ afterTick(() => {
322
+ this.parentMenu.root.ignoreCloseAutoFocus = false;
323
+ });
310
324
  }
311
325
  get shouldRender() {
312
326
  return this.parentMenu.contentPresence.shouldRender;
@@ -52,6 +52,11 @@
52
52
  enabled: boolean;
53
53
  contentPointerEvents?: "auto" | "none";
54
54
  } = $props();
55
+
56
+ const resolvedPreventScroll = $derived(preventScroll ?? true);
57
+ const effectiveStrategy = $derived(
58
+ strategy ?? (resolvedPreventScroll ? "fixed" : "absolute")
59
+ );
55
60
  </script>
56
61
 
57
62
  <PopperContent
@@ -68,7 +73,7 @@
68
73
  {sticky}
69
74
  {hideWhenDetached}
70
75
  {updatePositionStrategy}
71
- {strategy}
76
+ strategy={effectiveStrategy}
72
77
  {dir}
73
78
  {wrapperId}
74
79
  {style}
@@ -79,9 +84,9 @@
79
84
  >
80
85
  {#snippet content({ props: floatingProps, wrapperProps })}
81
86
  {#if restProps.forceMount && enabled}
82
- <ScrollLock {preventScroll} />
87
+ <ScrollLock preventScroll={resolvedPreventScroll} />
83
88
  {:else if !restProps.forceMount}
84
- <ScrollLock {preventScroll} />
89
+ <ScrollLock preventScroll={resolvedPreventScroll} />
85
90
  {/if}
86
91
  <FocusScope
87
92
  {onOpenAutoFocus}
@@ -23,6 +23,7 @@ export function useFloating(options) {
23
23
  let middlewareData = $state({});
24
24
  let isPositioned = $state(false);
25
25
  let hasWhileMountedPosition = false;
26
+ let updateRequestId = 0;
26
27
  const floatingStyles = $derived.by(() => {
27
28
  // preserve last known position when floating ref is null (during transitions)
28
29
  const xVal = floating.current ? roundByDPR(floating.current, x) : x;
@@ -50,12 +51,20 @@ export function useFloating(options) {
50
51
  function update() {
51
52
  if (reference.current === null || floating.current === null)
52
53
  return;
53
- computePosition(reference.current, floating.current, {
54
+ const referenceNode = reference.current;
55
+ const floatingNode = floating.current;
56
+ const requestId = ++updateRequestId;
57
+ computePosition(referenceNode, floatingNode, {
54
58
  middleware: middlewareOption,
55
59
  placement: placementOption,
56
60
  strategy: strategyOption,
57
61
  }).then((position) => {
58
- const referenceNode = reference.current;
62
+ // ignore stale async resolutions when newer updates were requested.
63
+ if (requestId !== updateRequestId)
64
+ return;
65
+ // ignore stale resolutions after ref replacement.
66
+ if (reference.current !== referenceNode || floating.current !== floatingNode)
67
+ return;
59
68
  const referenceHidden = isReferenceHidden(referenceNode);
60
69
  if (referenceHidden) {
61
70
  // keep last good coordinates when the anchor disappears to avoid
@@ -91,6 +100,7 @@ export function useFloating(options) {
91
100
  whileElementsMountedCleanup();
92
101
  whileElementsMountedCleanup = undefined;
93
102
  }
103
+ updateRequestId++;
94
104
  }
95
105
  function attach() {
96
106
  cleanup();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bits-ui",
3
- "version": "2.16.3",
3
+ "version": "2.16.4",
4
4
  "license": "MIT",
5
5
  "repository": "github:huntabyte/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",