Açıklama

Varsayılan sağ tıklama menüsünü (bağlam menüsü) geçersiz kılan ve özel tasarlanmış bir menü görüntüleyen bir kullanıcı arayüzü pasajı.

Özellikler

Uygulama noktaları

Konum hesaplamasında getBoundingClientRect() kullanarak ve tarayıcı penceresi boyutu ile tıklanan koordinatları karşılaştırarak menüyü ekrandan dışarı taşmayacak şekilde kontrol ediyoruz.

Right click anywhere in this area.

<div class="context-area">
  <div class="context-message">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
      <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
      <line x1="9" y1="3" x2="9" y2="21"></line>
    </svg>
    <p>Right click anywhere in this area.</p>
  </div>
</div>

<div class="custom-context-menu" id="context-menu">
  <ul class="menu-list">
    <li class="menu-item">
      <svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <polyline points="15 18 9 12 15 6"></polyline>
      </svg>
      <span>Back</span>
      <span class="menu-shortcut">Alt+Left</span>
    </li>
    <li class="menu-item">
      <svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <polyline points="9 18 15 12 9 6"></polyline>
      </svg>
      <span>Forward</span>
      <span class="menu-shortcut">Alt+Right</span>
    </li>
    <li class="menu-item">
      <svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <polyline points="23 4 23 10 17 10"></polyline>
        <polyline points="1 20 1 14 7 14"></polyline>
        <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
      </svg>
      <span>Reload</span>
      <span class="menu-shortcut">Ctrl+R</span>
    </li>
    <li class="menu-separator"></li>
    <li class="menu-item">
      <svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
        <polyline points="17 21 17 13 7 13 7 21"></polyline>
        <polyline points="7 3 7 8 15 8"></polyline>
      </svg>
      <span>Save As...</span>
      <span class="menu-shortcut">Ctrl+S</span>
    </li>
    <li class="menu-item">
      <svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <polyline points="6 9 6 2 18 2 18 9"></polyline>
        <path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path>
        <rect x="6" y="14" width="12" height="8"></rect>
      </svg>
      <span>Print...</span>
      <span class="menu-shortcut">Ctrl+P</span>
    </li>
  </ul>
</div>


.context-area {
  width: 100%;
  max-width: 600px;
  height: 400px;
  margin: 0 auto;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #ffffff;
  border: 2px dashed #cbd5e1;
  border-radius: 16px;
  color: #64748b;
  user-select: none;
  transition: border-color 0.3s ease;
}

.context-area:hover {
  border-color: #94a3b8;
}

.context-message {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.context-message p {
  margin: 0;
  font-weight: 500;
}

/* Context Menu */
.custom-context-menu {
  display: none;
  position: absolute;
  z-index: 9999;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 12px;
  box-shadow: 
    0 4px 6px -1px rgba(0, 0, 0, 0.05),
    0 10px 15px -3px rgba(0, 0, 0, 0.1),
    0 20px 25px -5px rgba(0, 0, 0, 0.05);
  padding: 6px;
  min-width: 240px;
  color: #1e293b;
  
  /* Initial hidden state for animation */
  visibility: hidden;
  opacity: 0;
  transform: scale(0.95);
  transform-origin: top left;
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
}

.custom-context-menu.active {
  visibility: visible;
  opacity: 1;
  transform: scale(1);
}

.menu-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.menu-item {
  display: flex;
  align-items: center;
  padding: 10px 12px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.15s ease;
}

.menu-item:hover {
  background-color: #f1f5f9;
  color: #0f172a;
}

.menu-icon {
  width: 16px;
  height: 16px;
  margin-right: 12px;
  color: #64748b;
  transition: color 0.15s ease;
}

.menu-item:hover .menu-icon {
  color: #3b82f6;
}

.menu-shortcut {
  margin-left: auto;
  font-size: 12px;
  color: #94a3b8;
  font-weight: 400;
  letter-spacing: 0.5px;
}

.menu-separator {
  height: 1px;
  background-color: #e2e8f0;
  margin: 6px 4px;
}
  function initContextMenu() {
    const contextArea = document.querySelector('.context-area');
    const contextMenu = document.getElementById('context-menu');
    if (!contextArea || !contextMenu) return;
    let isMenuVisible = false;

    const showMenu = (x, y) => {
      contextMenu.style.visibility = 'hidden';
      contextMenu.style.display = 'block';
      
      const menuRect = contextMenu.getBoundingClientRect();
      const wrapperRect = contextArea.parentElement.getBoundingClientRect();
      
      let localX = x - wrapperRect.left;
      let localY = y - wrapperRect.top;
      
      let finalX = localX;
      let finalY = localY;

      if (finalX + menuRect.width > wrapperRect.width) {
        finalX = wrapperRect.width - menuRect.width - 8;
      }
      
      if (finalY + menuRect.height > wrapperRect.height) {
        finalY = wrapperRect.height - menuRect.height - 8;
      }

      const originX = finalX < localX ? 'right' : 'left';
      const originY = finalY < localY ? 'bottom' : 'top';
      contextMenu.style.transformOrigin = originX + ' ' + originY;

      contextMenu.style.left = finalX + 'px';
      contextMenu.style.top = finalY + 'px';
      
      contextMenu.style.visibility = 'visible';
      
      requestAnimationFrame(() => {
        contextMenu.classList.add('active');
      });
      
      isMenuVisible = true;
    };

    const hideMenu = () => {
      if (isMenuVisible) {
        contextMenu.classList.remove('active');
        setTimeout(() => {
          if (!contextMenu.classList.contains('active')) {
            contextMenu.style.display = 'none';
          }
        }, 150);
        isMenuVisible = false;
      }
    };

    contextArea.addEventListener('contextmenu', (e) => {
      e.preventDefault();
      if (isMenuVisible) {
        contextMenu.classList.remove('active');
        setTimeout(() => { showMenu(e.clientX, e.clientY); }, 50);
      } else {
        showMenu(e.clientX, e.clientY);
      }
    });

    document.addEventListener('click', (e) => {
      if (isMenuVisible && !contextMenu.contains(e.target)) {
        hideMenu();
      }
    });

    document.addEventListener('scroll', () => { hideMenu(); }, { passive: true });

    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') hideMenu();
    });
    
    const menuItems = document.querySelectorAll('.menu-item');
    menuItems.forEach(item => {
      item.addEventListener('click', () => { hideMenu(); });
    });
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initContextMenu);
  } else {
    initContextMenu();
  }