diff --git a/packages/web/src/App.js b/packages/web/src/App.js index c2f975e76..b325a80ae 100644 --- a/packages/web/src/App.js +++ b/packages/web/src/App.js @@ -17,6 +17,7 @@ import UploadsProvider from './utility/UploadsProvider'; import ThemeHelmet from './themes/ThemeHelmet'; import PluginsProvider from './plugins/PluginsProvider'; import { ExtensionsProvider } from './utility/useExtensions'; +import { MenuLayerProvider } from './modals/showMenu'; function App() { return ( @@ -33,8 +34,10 @@ function App() { - - + + + + diff --git a/packages/web/src/Screen.js b/packages/web/src/Screen.js index f93a6b4d0..21876133d 100644 --- a/packages/web/src/Screen.js +++ b/packages/web/src/Screen.js @@ -15,6 +15,7 @@ import { ModalLayer } from './modals/showModal'; import DragAndDropFileTarget from './DragAndDropFileTarget'; import { useUploadsZone } from './utility/UploadsProvider'; import useTheme from './theme/useTheme'; +import { MenuLayer } from './modals/showMenu'; const BodyDiv = styled.div` position: fixed; @@ -132,6 +133,7 @@ export default function Screen() { + diff --git a/packages/web/src/TabsPanel.js b/packages/web/src/TabsPanel.js index da446e23b..c392cc66b 100644 --- a/packages/web/src/TabsPanel.js +++ b/packages/web/src/TabsPanel.js @@ -4,11 +4,11 @@ import styled from 'styled-components'; import { DropDownMenuItem, DropDownMenuDivider } from './modals/DropDownMenu'; import { useOpenedTabs, useSetOpenedTabs, useCurrentDatabase, useSetCurrentDatabase } from './utility/globalState'; -import { showMenu } from './modals/DropDownMenu'; import { getConnectionInfo } from './utility/metadataLoaders'; import { FontIcon } from './icons'; import useTheme from './theme/useTheme'; import usePropsCompare from './utility/usePropsCompare'; +import { useShowMenu } from './modals/showMenu'; // const files = [ // { name: 'app.js' }, @@ -126,6 +126,7 @@ function getDbIcon(key) { export default function TabsPanel() { // const formatDbKey = (conid, database) => `${database}-${conid}`; const theme = useTheme(); + const showMenu = useShowMenu(); const tabs = useOpenedTabs(); const setOpenedTabs = useSetOpenedTabs(); diff --git a/packages/web/src/appobj/AppObjects.js b/packages/web/src/appobj/AppObjects.js index 249b5ee26..4e82fec82 100644 --- a/packages/web/src/appobj/AppObjects.js +++ b/packages/web/src/appobj/AppObjects.js @@ -4,7 +4,7 @@ import _ from 'lodash'; import React from 'react'; import styled from 'styled-components'; import { FontIcon } from '../icons'; -import { showMenu } from '../modals/DropDownMenu'; +// import { showMenu } from '../modals/DropDownMenu'; import useTheme from '../theme/useTheme'; import { useSetOpenedTabs, useAppObjectParams } from '../utility/globalState'; @@ -60,7 +60,7 @@ export function AppObjectCore({ if (!Menu) return; event.preventDefault(); - showMenu(event.pageX, event.pageY, ); + // showMenu(event.pageX, event.pageY, ); }; const Component = component == 'div' ? AppObjectDiv : AppObjectSpan; diff --git a/packages/web/src/appobj/databaseAppObject.js b/packages/web/src/appobj/databaseAppObject.js index 7e3db8f17..5c8b24657 100644 --- a/packages/web/src/appobj/databaseAppObject.js +++ b/packages/web/src/appobj/databaseAppObject.js @@ -4,8 +4,10 @@ import { DropDownMenuItem } from '../modals/DropDownMenu'; import { openNewTab } from '../utility/common'; import ImportExportModal from '../modals/ImportExportModal'; import { getDefaultFileFormat } from '../utility/fileformats'; +import { useSetOpenedTabs } from '../utility/globalState'; -function Menu({ data, setOpenedTabs, showModal, extensions }) { +function Menu({ data, showModal, extensions }) { + const setOpenedTabs = useSetOpenedTabs(); const { connection, name } = data; const tooltip = `${connection.displayName || connection.server}\n${name}`; diff --git a/packages/web/src/datagrid/DataFilterControl.js b/packages/web/src/datagrid/DataFilterControl.js index 4d272277d..0729649c7 100644 --- a/packages/web/src/datagrid/DataFilterControl.js +++ b/packages/web/src/datagrid/DataFilterControl.js @@ -1,6 +1,6 @@ // @ts-nocheck import React from 'react'; -import { DropDownMenuItem, DropDownMenuDivider, showMenu } from '../modals/DropDownMenu'; +import { DropDownMenuItem, DropDownMenuDivider } from '../modals/DropDownMenu'; import styled from 'styled-components'; import keycodes from '../utility/keycodes'; import { parseFilter, createMultiLineFilter } from 'dbgate-filterparser'; @@ -10,6 +10,7 @@ import FilterMultipleValuesModal from '../modals/FilterMultipleValuesModal'; import SetFilterModal from '../modals/SetFilterModal'; import { FontIcon } from '../icons'; import useTheme from '../theme/useTheme'; +import { useShowMenu } from '../modals/showMenu'; // import { $ } from '../../Utility/jquery'; // import autobind from 'autobind-decorator'; // import * as React from 'react'; @@ -182,6 +183,7 @@ export default function DataFilterControl({ onFocusGrid, }) { const showModal = useShowModal(); + const showMenu = useShowMenu(); const theme = useTheme(); const [filterState, setFilterState] = React.useState('empty'); const setFilterText = (filter) => { diff --git a/packages/web/src/datagrid/DataGridCore.js b/packages/web/src/datagrid/DataGridCore.js index 853cbc02c..a2c1b10a0 100644 --- a/packages/web/src/datagrid/DataGridCore.js +++ b/packages/web/src/datagrid/DataGridCore.js @@ -22,7 +22,6 @@ import DataGridToolbar from './DataGridToolbar'; // import usePropsCompare from '../utility/usePropsCompare'; import ColumnHeaderControl from './ColumnHeaderControl'; import InlineButton from '../widgets/InlineButton'; -import { showMenu } from '../modals/DropDownMenu'; import DataGridContextMenu from './DataGridContextMenu'; import LoadingInfo from '../widgets/LoadingInfo'; import ErrorInfo from '../widgets/ErrorInfo'; @@ -30,6 +29,7 @@ import { openNewTab } from '../utility/common'; import { useSetOpenedTabs } from '../utility/globalState'; import { FontIcon } from '../icons'; import useTheme from '../theme/useTheme'; +import { useShowMenu } from '../modals/showMenu'; const GridContainer = styled.div` position: absolute; @@ -138,6 +138,7 @@ export default function DataGridCore(props) { const [autofillDragStartCell, setAutofillDragStartCell] = React.useState(nullCell); const [autofillSelectedCells, setAutofillSelectedCells] = React.useState(emptyCellArray); const [focusFilterInputs, setFocusFilterInputs] = React.useState({}); + const showMenu = useShowMenu(); const autofillMarkerCell = React.useMemo( () => diff --git a/packages/web/src/modals/DropDownMenu.js b/packages/web/src/modals/DropDownMenu.js index b3651f2a5..2302ff948 100644 --- a/packages/web/src/modals/DropDownMenu.js +++ b/packages/web/src/modals/DropDownMenu.js @@ -2,6 +2,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import styled from 'styled-components'; import { LoadingToken, sleep } from '../utility/common'; +import useDocumentClick from '../utility/useDocumentClick'; +import { useHideMenu } from './showMenu'; const ContextMenuStyled = styled.ul` position: absolute; @@ -167,6 +169,11 @@ export function DropDownMenuItem({ children, keyText = undefined, onClick }) { // } export function ContextMenu({ left, top, children }) { + const hideMenu = useHideMenu(); + useDocumentClick(async () => { + await sleep(0); + hideMenu(); + }); return {children}; } @@ -204,71 +211,71 @@ export function ContextMenu({ left, top, children }) { // parentMenu: PropTypes.any // }; -let menuHandle = null; -let hideToken = null; +// let menuHandle = null; +// let hideToken = null; -function showMenuCore(left, top, contentHolder, closeCallback = null) { - let container = document.createElement('div'); - let handle = { - container, - closeCallback, - close() { - this.container.remove(); - }, - }; - document.body.appendChild(container); - ReactDOM.render( - - {contentHolder} - , - container - ); - return handle; -} +// function showMenuCore(left, top, contentHolder, closeCallback = null) { +// let container = document.createElement('div'); +// let handle = { +// container, +// closeCallback, +// close() { +// this.container.remove(); +// }, +// }; +// document.body.appendChild(container); +// ReactDOM.render( +// +// {contentHolder} +// , +// container +// ); +// return handle; +// } -export function showMenu(left, top, contentHolder, closeCallback = null) { - hideMenu(); - if (hideToken) hideToken.cancel(); - menuHandle = showMenuCore(left, top, contentHolder, closeCallback); - captureMouseDownEvents(); -} +// export function showMenu(left, top, contentHolder, closeCallback = null) { +// hideMenu(); +// if (hideToken) hideToken.cancel(); +// menuHandle = showMenuCore(left, top, contentHolder, closeCallback); +// captureMouseDownEvents(); +// } -function captureMouseDownEvents() { - document.addEventListener('mousedown', mouseDownListener, true); -} +// function captureMouseDownEvents() { +// document.addEventListener('mousedown', mouseDownListener, true); +// } -function releaseMouseDownEvents() { - document.removeEventListener('mousedown', mouseDownListener, true); -} +// function releaseMouseDownEvents() { +// document.removeEventListener('mousedown', mouseDownListener, true); +// } -function captureMouseUpEvents() { - document.addEventListener('mouseup', mouseUpListener, true); -} +// function captureMouseUpEvents() { +// document.addEventListener('mouseup', mouseUpListener, true); +// } -function releaseMouseUpEvents() { - document.removeEventListener('mouseup', mouseUpListener, true); -} +// function releaseMouseUpEvents() { +// document.removeEventListener('mouseup', mouseUpListener, true); +// } -async function mouseDownListener(e) { - captureMouseUpEvents(); -} +// async function mouseDownListener(e) { +// captureMouseUpEvents(); +// } -async function mouseUpListener(e) { - let token = new LoadingToken(); - hideToken = token; - await sleep(0); - if (token.isCanceled) return; - hideMenu(); -} +// async function mouseUpListener(e) { +// let token = new LoadingToken(); +// hideToken = token; +// await sleep(0); +// if (token.isCanceled) return; +// hideMenu(); +// } -function hideMenu() { - if (menuHandle == null) return; - menuHandle.close(); - if (menuHandle.closeCallback) menuHandle.closeCallback(); - menuHandle = null; - releaseMouseDownEvents(); - releaseMouseUpEvents(); -} +// function hideMenu() { +// if (menuHandle == null) return; +// menuHandle.close(); +// if (menuHandle.closeCallback) menuHandle.closeCallback(); +// menuHandle = null; +// releaseMouseDownEvents(); +// releaseMouseUpEvents(); +// } function getElementOffset(element) { var de = document.documentElement; diff --git a/packages/web/src/modals/showMenu.js b/packages/web/src/modals/showMenu.js new file mode 100644 index 000000000..6ac91097a --- /dev/null +++ b/packages/web/src/modals/showMenu.js @@ -0,0 +1,40 @@ +import React from 'react'; +import { ContextMenu } from './DropDownMenu'; +import uuidv1 from 'uuid/v1'; + +const Context = React.createContext(null); + +export function MenuLayerProvider({ children }) { + const [menu, setMenu] = React.useState(null); + return {children}; +} + +export function MenuLayer() { + const [menu] = React.useContext(Context); + return ( +
+ {menu != null && ( + + {menu.menu} + + )} +
+ ); +} + +export function useHideMenu() { + const [, setMenu] = React.useContext(Context); + return () => setMenu(null); +} + +export function useShowMenu() { + const [, setMenu] = React.useContext(Context); + const showMenu = (left, top, menu) => { + const menuid = uuidv1(); + setMenu({ menuid, left, top, menu }); + }; + return showMenu; + // const container = document.createElement('div'); + // document.body.appendChild(container); + // ReactDOM.render(, container); +} diff --git a/packages/web/src/utility/useDocumentClick.js b/packages/web/src/utility/useDocumentClick.js new file mode 100644 index 000000000..3c0fd011b --- /dev/null +++ b/packages/web/src/utility/useDocumentClick.js @@ -0,0 +1,20 @@ +import React from 'react'; + +export default function useDocumentClick(callback) { + const mouseUpListener = React.useCallback((e) => { + callback(); + document.removeEventListener('mouseup', mouseUpListener, true); + }, []); + const mouseDownListener = React.useCallback((e) => { + document.addEventListener('mouseup', mouseUpListener, true); + document.removeEventListener('mousedown', mouseDownListener, true); + }, []); + + React.useEffect(() => { + document.addEventListener('mousedown', mouseDownListener, true); + return () => { + document.removeEventListener('mouseup', mouseUpListener, true); + document.removeEventListener('mousedown', mouseDownListener, true); + }; + }, []); +} diff --git a/packages/web/src/widgets/DropDownButton.js b/packages/web/src/widgets/DropDownButton.js index 388f21903..c1d725fea 100644 --- a/packages/web/src/widgets/DropDownButton.js +++ b/packages/web/src/widgets/DropDownButton.js @@ -1,10 +1,11 @@ import React from 'react'; import { FontIcon } from '../icons'; -import { showMenu } from '../modals/DropDownMenu'; +import { useShowMenu } from '../modals/showMenu'; import InlineButton from './InlineButton'; export default function DropDownButton({ children }) { const buttonRef = React.useRef(null); + const showMenu = useShowMenu(); const handleShowMenu = () => { const rect = buttonRef.current.getBoundingClientRect();