infinity-ui-elements 1.9.25 → 1.9.26

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/dist/index.js CHANGED
@@ -3184,12 +3184,14 @@ const Dropdown = React__namespace.forwardRef(({ className, trigger, items = [],
3184
3184
  const isOpen = controlledOpen !== undefined ? controlledOpen : uncontrolledOpen;
3185
3185
  const dropdownRef = React__namespace.useRef(null);
3186
3186
  const menuRef = React__namespace.useRef(null);
3187
+ const VIEWPORT_PADDING = 8;
3188
+ const MENU_MAX_HEIGHT = 400;
3187
3189
  const [menuPosition, setMenuPosition] = React__namespace.useState({
3188
3190
  top: "100%",
3189
3191
  bottom: "auto",
3190
3192
  left: "0",
3191
3193
  right: "auto",
3192
- maxHeight: "400px",
3194
+ maxHeight: `${MENU_MAX_HEIGHT}px`,
3193
3195
  });
3194
3196
  // Detect if we're on mobile (< 768px)
3195
3197
  const [isMobile, setIsMobile] = React__namespace.useState(false);
@@ -3248,68 +3250,56 @@ const Dropdown = React__namespace.forwardRef(({ className, trigger, items = [],
3248
3250
  const calculatePosition = () => {
3249
3251
  const triggerRect = dropdownRef.current.getBoundingClientRect();
3250
3252
  const menuElement = menuRef.current;
3251
- // Get menu dimensions (use a temporary measurement if needed)
3253
+ const currentContainerRect = dropdownRef.current.getBoundingClientRect();
3252
3254
  const menuRect = menuElement.getBoundingClientRect();
3253
- const menuHeight = menuRect.height || 400; // fallback to max-height
3254
- const menuWidth = menuRect.width;
3255
+ const menuHeight = menuRect.height || MENU_MAX_HEIGHT;
3256
+ const menuWidth = menuRect.width || 320; // fallback for initial render (w-80 = 320px)
3255
3257
  const viewportHeight = window.innerHeight;
3256
3258
  const viewportWidth = window.innerWidth;
3257
- const spaceBelow = viewportHeight - triggerRect.bottom;
3258
- const spaceAbove = triggerRect.top;
3259
- const spaceRight = viewportWidth - triggerRect.left;
3260
- const spaceLeft = triggerRect.right;
3259
+ const spaceBelow = viewportHeight - triggerRect.bottom - VIEWPORT_PADDING;
3260
+ const spaceAbove = triggerRect.top - VIEWPORT_PADDING;
3261
3261
  const position = {
3262
- top: "auto",
3263
- bottom: "auto",
3264
- left: "auto",
3265
- right: "auto",
3266
- maxHeight: "400px",
3262
+ maxHeight: `${MENU_MAX_HEIGHT}px`,
3267
3263
  };
3268
- // Vertical positioning
3264
+ // Vertical positioning: prefer below, flip above if not enough space
3269
3265
  if (spaceBelow >= menuHeight || spaceBelow >= spaceAbove) {
3270
- // Position below trigger
3271
3266
  position.top = "100%";
3272
3267
  position.bottom = "auto";
3273
- position.maxHeight = `${Math.min(400, spaceBelow - 16)}px`;
3268
+ position.maxHeight = `${Math.min(MENU_MAX_HEIGHT, Math.max(0, spaceBelow))}px`;
3274
3269
  }
3275
3270
  else {
3276
- // Position above trigger
3277
3271
  position.top = "auto";
3278
3272
  position.bottom = "100%";
3279
- position.maxHeight = `${Math.min(400, spaceAbove - 16)}px`;
3280
- }
3281
- // Horizontal positioning
3282
- if (spaceRight >= menuWidth) {
3283
- // Align to left edge of trigger
3284
- position.left = "0";
3285
- position.right = "auto";
3286
- }
3287
- else if (spaceLeft >= menuWidth) {
3288
- // Align to right edge of trigger
3289
- position.left = "auto";
3290
- position.right = "0";
3291
- }
3292
- else {
3293
- // Not enough space on either side, try to center or align based on available space
3294
- if (triggerRect.left + menuWidth > viewportWidth) {
3295
- position.left = "auto";
3296
- position.right = "0";
3297
- }
3298
- else {
3299
- position.left = "0";
3300
- position.right = "auto";
3301
- }
3273
+ position.maxHeight = `${Math.min(MENU_MAX_HEIGHT, Math.max(0, spaceAbove))}px`;
3302
3274
  }
3275
+ // Horizontal: clamp menu left so it stays within viewport
3276
+ const minMenuLeft = VIEWPORT_PADDING;
3277
+ const maxMenuLeft = viewportWidth - menuWidth - VIEWPORT_PADDING;
3278
+ const desiredMenuLeft = Math.max(minMenuLeft, Math.min(maxMenuLeft, triggerRect.left));
3279
+ position.left = `${desiredMenuLeft - currentContainerRect.left}px`;
3280
+ position.right = "auto";
3303
3281
  setMenuPosition(position);
3304
3282
  };
3305
- // Calculate position after menu is rendered
3306
- calculatePosition();
3307
- // Recalculate on window resize or scroll
3308
- const handleResize = () => calculatePosition();
3309
- const handleScroll = () => calculatePosition();
3283
+ // Run after layout so menu dimensions are correct (double rAF for paint)
3284
+ let cancelled = false;
3285
+ const scheduleUpdate = () => {
3286
+ requestAnimationFrame(() => {
3287
+ requestAnimationFrame(() => {
3288
+ if (!cancelled &&
3289
+ dropdownRef.current &&
3290
+ menuRef.current) {
3291
+ calculatePosition();
3292
+ }
3293
+ });
3294
+ });
3295
+ };
3296
+ scheduleUpdate();
3297
+ const handleResize = () => scheduleUpdate();
3298
+ const handleScroll = () => scheduleUpdate();
3310
3299
  window.addEventListener("resize", handleResize);
3311
3300
  window.addEventListener("scroll", handleScroll, true);
3312
3301
  return () => {
3302
+ cancelled = true;
3313
3303
  window.removeEventListener("resize", handleResize);
3314
3304
  window.removeEventListener("scroll", handleScroll, true);
3315
3305
  };