diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js index c9993b7a0b..991af6ae22 100644 --- a/src/components/BrowserRow/BrowserRow.react.js +++ b/src/components/BrowserRow/BrowserRow.react.js @@ -92,7 +92,7 @@ export default class BrowserRow extends Component { > selectRow(obj.id, e.target.checked)} onMouseDown={e => onMouseDownRowCheckBox(e.target.checked)} /> diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 1d5cd34da1..b60df5be4a 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -38,11 +38,86 @@ import subscribeTo from 'lib/subscribeTo'; import * as ColumnPreferences from 'lib/ColumnPreferences'; import * as ClassPreferences from 'lib/ClassPreferences'; import { Helmet } from 'react-helmet'; +import { useBeforeUnload } from 'react-router-dom'; import generatePath from 'lib/generatePath'; import { withRouter } from 'lib/withRouter'; import { get } from 'lib/AJAX'; import BrowserFooter from './BrowserFooter.react'; +const SELECTED_ROWS_MESSAGE = + 'There are selected rows. Are you sure you want to leave this page?'; + +function SelectedRowsNavigationPrompt({ when }) { + const message = SELECTED_ROWS_MESSAGE; + + React.useEffect(() => { + if (!when) { + return; + } + + const handleBeforeUnload = event => { + event.preventDefault(); + event.returnValue = message; + return message; + }; + + const handleLinkClick = event => { + if (event.defaultPrevented) { + return; + } + if (event.button !== 0) { + return; + } + if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) { + return; + } + const anchor = event.target.closest('a[href]'); + if (!anchor || anchor.target === '_blank') { + return; + } + const href = anchor.getAttribute('href'); + if (!href || href === '#') { + return; + } + if (!window.confirm(message)) { + event.preventDefault(); + event.stopPropagation(); + } + }; + + const handlePopState = () => { + if (!window.confirm(message)) { + window.history.go(1); + } + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + document.addEventListener('click', handleLinkClick, true); + window.addEventListener('popstate', handlePopState); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + document.removeEventListener('click', handleLinkClick, true); + window.removeEventListener('popstate', handlePopState); + }; + }, [when, message]); + + useBeforeUnload( + React.useCallback( + event => { + if (when) { + event.preventDefault(); + event.returnValue = message; + return message; + } + }, + [when, message] + ) + ); + + return null; +} + // The initial and max amount of rows fetched by lazy loading const BROWSER_LAST_LOCATION = 'brower_last_location'; @@ -879,6 +954,11 @@ class Browser extends DashboardView { } async refresh() { + if (Object.keys(this.state.selection).length > 0) { + if (!window.confirm(SELECTED_ROWS_MESSAGE)) { + return; + } + } const relation = this.state.relation; const prevFilters = this.state.filters || new List(); const initialState = { @@ -2446,6 +2526,9 @@ class Browser extends DashboardView { {pageTitle} + 0} + /> {browser} {notification} {extras}