| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- import { useEffect, useRef } from 'react'
- import ReactDOM from 'react-dom'
- import { useTranslation } from 'react-i18next'
- import {
- Dropdown,
- DropdownItem,
- DropdownMenu,
- } from '@/shared/components/dropdown/dropdown-menu'
- import { useTabsContext } from '@/features/ide-react/context/tabs-context'
- export function TabsContextMenu() {
- const { t } = useTranslation()
- const {
- tabs,
- closeTab,
- closeOtherTabs,
- contextMenuTarget,
- setContextMenuTarget,
- } = useTabsContext()
- const menuRef = useRef<HTMLDivElement>(null)
- // Close the context menu when opening another one
- useEffect(() => {
- if (!contextMenuTarget) return
- const handler = (event: MouseEvent) => {
- if (event.button !== 2) return
- const target = event.target as Element | null
- // Right-clicking another tab should re-open the menu on that tab;
- // its onContextMenu handler will set the new target.
- if (target?.closest('.context-menu')) return
- if (target?.closest('.editor-file-tab')) return
- setContextMenuTarget(null)
- }
- // Listen on mousedown (not contextmenu) so we still close when another
- // component's handler calls preventDefault on the contextmenu event.
- document.addEventListener('mousedown', handler)
- return () => document.removeEventListener('mousedown', handler)
- }, [contextMenuTarget, setContextMenuTarget])
- // Move focus to the menu on open, and back to the originating tab on close.
- const lastTabIdRef = useRef<string | null>(null)
- useEffect(() => {
- if (contextMenuTarget) {
- lastTabIdRef.current = contextMenuTarget.tabId
- menuRef.current?.querySelector<HTMLElement>('[role="menu"]')?.focus()
- } else if (lastTabIdRef.current) {
- document
- .querySelector<HTMLElement>(`[data-tab-id="${lastTabIdRef.current}"]`)
- ?.focus()
- lastTabIdRef.current = null
- }
- }, [contextMenuTarget])
- if (!contextMenuTarget) return null
- const close = () => setContextMenuTarget(null)
- const handleKeyDown = (event: React.KeyboardEvent<Element>) => {
- if (event.key === 'Tab' || event.key === 'Escape') {
- event.preventDefault()
- close()
- }
- }
- const handleToggle = (wantOpen: boolean) => {
- if (!wantOpen) close()
- }
- return ReactDOM.createPortal(
- <div
- ref={menuRef}
- style={{ top: contextMenuTarget.top, left: contextMenuTarget.left }}
- // TODO ide-redesign-cleanup: remove 'ide-redesign-main' class when old editor is removed
- className="context-menu ide-redesign-main"
- >
- <Dropdown
- show
- drop="down"
- onKeyDown={handleKeyDown}
- onToggle={handleToggle}
- >
- <DropdownMenu className="dropdown-menu-sm-width" tabIndex={-1}>
- <DropdownItem
- disabled={tabs.length <= 1}
- as="button"
- onClick={() => {
- closeTab(contextMenuTarget.tabId)
- close()
- }}
- >
- {t('close_tab')}
- </DropdownItem>
- <DropdownItem
- disabled={tabs.length <= 1}
- as="button"
- onClick={() => {
- closeOtherTabs(contextMenuTarget.tabId)
- close()
- }}
- >
- {t('close_others')}
- </DropdownItem>
- </DropdownMenu>
- </Dropdown>
- </div>,
- document.body
- )
- }
|