@cosxai/ui 0.2.6 → 0.2.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosxai/ui",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "COSX design system — React 19 component primitives shared across product-meta and other consumers",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -37,9 +37,10 @@ import { cn } from "../lib/cn";
37
37
  // Popover ALWAYS renders via createPortal to document.body so it
38
38
  // escapes Card / Drawer / Dialog parents whose overflow:hidden
39
39
  // would otherwise clip it. Position is computed against the
40
- // trigger's bounding rect and recomputed on resize; we
41
- // intentionally close on scroll to avoid the "popover drifts off
42
- // the trigger" failure mode (matches Radix / Headless UI default).
40
+ // trigger's bounding rect and re-computed on page scroll + window
41
+ // resize so the popover tracks the trigger. Scrolls INSIDE the
42
+ // popover (the option list / search input) are filtered out — only
43
+ // outer scrolls reposition.
43
44
  //
44
45
  // Keyboard model (mirrors ARIA 1.2 combobox-as-listbox spec):
45
46
  // trigger CLOSED:
@@ -208,8 +209,19 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(function Select
208
209
 
209
210
  useEffect(() => {
210
211
  if (!open) return;
211
- const onResize = () => setRect(computeRect());
212
- const onScroll = () => setOpen(false);
212
+ const reposition = () => {
213
+ const r = computeRect();
214
+ if (r) setRect(r);
215
+ };
216
+ const onResize = reposition;
217
+ const onScroll = (e: Event) => {
218
+ // Scrolls INSIDE the popover (the option list / search input)
219
+ // must not re-trigger positioning — they're not page scrolls.
220
+ // capture=true catches them before they bubble; we filter here.
221
+ const target = e.target as Node | null;
222
+ if (target && popoverRef.current?.contains(target)) return;
223
+ reposition();
224
+ };
213
225
  window.addEventListener("resize", onResize);
214
226
  window.addEventListener("scroll", onScroll, true);
215
227
  return () => {