import { useState, useRef, useEffect } from 'react'; import { FaCaretRight } from 'react-icons/fa'; import './PozoMenu.scss'; import { getSession, sessionStore } from '../../Services/Others'; const PozoMenu = ({ items = [], mode = 'vertical', style = {}, onClick, collapse = false, selectedKey: propSelectedKey, isBottomMenu = false, }) => { const [openKeys, setOpenKeys] = useState([]); const [selectedKey, setSelectedKey] = useState(''); const [dropdownPositions, setDropdownPositions] = useState({}); const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); const BranchId = getSession('BranchId'); const menuRef = useRef(); // Update selected key when prop changes useEffect(() => { if (propSelectedKey !== undefined) { setSelectedKey(propSelectedKey); } }, [propSelectedKey]); // Close dropdown when clicking outside, window resize, or scroll useEffect(() => { const handleClickOutside = (event) => { if (menuRef.current && !menuRef.current.contains(event.target)) { setOpenKeys([]); setDropdownPositions({}); } }; const handleResize = () => { // Update mobile detection setIsMobile(window.innerWidth <= 768); // Recalculate positions on window resize if (openKeys.length > 0) { setOpenKeys([]); setDropdownPositions({}); } }; const handleScroll = () => { // 🎯 FIXED: Recalculate positions on scroll to maintain proper positioning if (openKeys.length > 0) { // Find all currently open menu elements and recalculate their positions const newPositions = {}; openKeys.forEach((key) => { const element = document.querySelector(`[data-menu-key="${key}"]`); if (element) { const level = key.split('-').length - 1; newPositions[key] = calculatePosition(element, level); } }); setDropdownPositions((prev) => ({ ...prev, ...newPositions })); } }; document.addEventListener('mousedown', handleClickOutside); window.addEventListener('resize', handleResize); window.addEventListener('scroll', handleScroll, true); // Capture scroll events return () => { document.removeEventListener('mousedown', handleClickOutside); window.removeEventListener('resize', handleResize); window.removeEventListener('scroll', handleScroll, true); }; }, [openKeys]); // 🎯 FIXED: Smart positioning calculation - exactly like your image behavior const calculatePosition = (element, level = 0) => { const rect = element.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; const scrollX = window.pageXOffset || document.documentElement.scrollLeft; const scrollY = window.pageYOffset || document.documentElement.scrollTop; // Dynamic dropdown dimensions - responsive const dropdownWidth = isMobile ? Math.min(280, viewportWidth - 20) : level > 0 ? 220 : 250; const dropdownHeight = Math.min( isMobile ? viewportHeight * 0.5 : 400, viewportHeight * 0.6 ); const gap = isMobile ? 2 : 4; const edgePadding = isMobile ? 10 : 8; // Calculate available space on both sides const spaceRight = viewportWidth - rect.right; const spaceLeft = rect.left; const spaceNeeded = dropdownWidth + gap + edgePadding; let position = { top: rect.top + scrollY, left: rect.right + gap + scrollX, direction: 'right', verticalDirection: 'down', }; // 🎯 FIXED: HORIZONTAL POSITIONING - Exactly like your images if (level === 0) { console.log('🎯 Space Analysis:', { spaceRight, spaceLeft, spaceNeeded, elementPosition: { left: rect.left, right: rect.right }, }); if (spaceRight >= spaceNeeded) { // Image 1: Enough space on right - open to RIGHT position.left = rect.right + gap + scrollX; position.direction = 'right'; console.log('✅ Opening RIGHT - space available'); } else if (spaceLeft >= spaceNeeded) { // Image 2: No space on right, space on left - open to LEFT position.left = rect.left - dropdownWidth - gap + scrollX; position.direction = 'left'; } else { // Edge case: Limited space on both sides if (spaceRight >= spaceLeft) { position.left = viewportWidth - dropdownWidth - edgePadding + scrollX; position.direction = 'right-edge'; } else { position.left = edgePadding + scrollX; position.direction = 'left-edge'; } console.log('⚠️ Edge positioning applied'); } } else { // Nested menu positioning const parentKeys = Object.keys(dropdownPositions); const parentDirection = parentKeys.length > 0 ? dropdownPositions[parentKeys[level - 1]]?.direction || 'right' : 'right'; if (parentDirection.includes('right')) { if (spaceRight >= spaceNeeded) { position.left = rect.right + gap + scrollX; position.direction = 'right'; } else { position.left = rect.left - dropdownWidth - gap + scrollX; position.direction = 'left'; } } else { if (spaceLeft >= spaceNeeded) { position.left = rect.left - dropdownWidth - gap + scrollX; position.direction = 'left'; } else { position.left = rect.right + gap + scrollX; position.direction = 'right'; } } } // 🎯 FIXED: VERTICAL POSITIONING const spaceBelow = viewportHeight - rect.bottom; const spaceAbove = rect.top; if (isBottomMenu && level === 0) { if (spaceAbove >= dropdownHeight + edgePadding) { position.top = rect.top - dropdownHeight - gap + scrollY; position.verticalDirection = 'up'; } else { position.top = rect.bottom + gap + scrollY; position.verticalDirection = 'down'; } } else { if (spaceBelow >= dropdownHeight + edgePadding) { position.top = rect.top + scrollY; position.verticalDirection = 'down'; } else if (spaceAbove >= dropdownHeight + edgePadding) { position.top = rect.bottom - dropdownHeight + scrollY; position.verticalDirection = 'up'; } else { position.top = Math.max(edgePadding, (viewportHeight - dropdownHeight) / 2) + scrollY; position.verticalDirection = 'center'; } } // 🎯 FIXED: Final boundary checks position.left = Math.max( edgePadding + scrollX, Math.min( position.left, viewportWidth - dropdownWidth - edgePadding + scrollX ) ); position.top = Math.max( edgePadding + scrollY, Math.min( position.top, viewportHeight - dropdownHeight - edgePadding + scrollY ) ); // Store dimensions for reference position.width = dropdownWidth; position.height = dropdownHeight; console.log('🎯 FIXED Position:', { clicked: { top: rect.top, left: rect.left, right: rect.right, bottom: rect.bottom, }, spaces: { right: spaceRight, left: spaceLeft, above: spaceAbove, below: spaceBelow, }, calculated: position, direction: position.direction, }); return position; }; // Handle menu item click const handleMenuClick = (item, event, parentKeys = []) => { if (item?.label === 'Branch Signin') { if (BranchId) { sessionStorage.removeItem('BranchId'); } } event.stopPropagation(); if (item.children && item.children.length > 0) { // Has children - toggle submenu const itemKey = item.key; const isOpen = openKeys.includes(itemKey); if (isOpen) { // Close this submenu and all its children setOpenKeys((prev) => prev.filter((key) => { // Keep only keys that are not this item or its descendants return !key.startsWith(itemKey) && key !== itemKey; }) ); setDropdownPositions((prev) => { const newPos = { ...prev }; Object.keys(newPos).forEach((key) => { if (key.startsWith(itemKey) || key === itemKey) { delete newPos[key]; } }); return newPos; }); } else { // Close all submenus at the same level or deeper const currentPath = [...parentKeys, itemKey]; const newOpenKeys = openKeys.filter((key) => { // Keep only parent keys in the current path return ( currentPath.some((pathKey) => key === pathKey) || currentPath.every((pathKey, index) => { const keyParts = key.split('-'); const pathParts = pathKey.split('-'); return ( index < keyParts.length && keyParts[index] === pathParts[index] ); }) ); }); // Add current item to open keys newOpenKeys.push(itemKey); // Calculate position and add to dropdown positions const position = calculatePosition( event.currentTarget, parentKeys.length ); setDropdownPositions((prev) => { // Remove positions for closed items const newPos = {}; newOpenKeys.forEach((key) => { if (prev[key]) newPos[key] = prev[key]; }); newPos[itemKey] = position; return newPos; }); setOpenKeys(newOpenKeys); } } else { // No children - select item and close all dropdowns setSelectedKey(item.key); setOpenKeys([]); setDropdownPositions({}); // Fullscreen on Sales Entry click if (item.label === 'Sales Entry') { if (!document.fullscreenElement) { document.documentElement.requestFullscreen().catch(() => {}); } } // Trigger onClick callback if provided if (onClick) { onClick({ key: item.key, keyPath: [...parentKeys, item.key], item: item, domEvent: event, }); } } }; // Check if item is selected const isSelected = (key) => selectedKey === key; // Check if item is open const isOpen = (key) => openKeys.includes(key); // Check if item is in active path (has selected child) const isInActivePath = (item, parentKeys = []) => { if (!item.children) return false; const checkChildren = (children, currentPath) => { return children.some((child) => { const childPath = [...currentPath, child.key]; if (child.key === selectedKey) return true; if (child.children) { return checkChildren(child.children, childPath); } return false; }); }; return checkChildren(item.children, [...parentKeys, item.key]); }; const menuRefs = useRef({}); const submenuRefs = useRef({}); const [positions, setPositions] = useState({}); const renderMenuItems = (items, parentKeys = [], level = 0) => { return items.map((item) => { if (!item) return null; const hasChildren = item.children && item.children.length > 0; const isItemSelected = isSelected(item.key); const isItemOpen = isOpen(item.key); const isItemInActivePath = isInActivePath(item, parentKeys); const currentKeys = [...parentKeys, item.key]; return (