diff --git a/packages/react-devtools-core/webpack.standalone.js b/packages/react-devtools-core/webpack.standalone.js index 8f5b4e6dcac935..199e6e2c46620f 100644 --- a/packages/react-devtools-core/webpack.standalone.js +++ b/packages/react-devtools-core/webpack.standalone.js @@ -18,6 +18,15 @@ const __DEV__ = NODE_ENV === 'development'; const DEVTOOLS_VERSION = getVersionString(); +const babelOptions = { + configFile: resolve( + __dirname, + '..', + 'react-devtools-shared', + 'babel.config.js', + ), +}; + module.exports = { mode: __DEV__ ? 'development' : 'production', devtool: __DEV__ ? 'cheap-module-eval-source-map' : 'source-map', @@ -62,17 +71,25 @@ module.exports = { ], module: { rules: [ + { + test: /\.worker\.js$/, + use: [ + { + loader: 'worker-loader', + options: { + inline: 'no-fallback', + }, + }, + { + loader: 'babel-loader', + options: babelOptions, + }, + ], + }, { test: /\.js$/, loader: 'babel-loader', - options: { - configFile: resolve( - __dirname, - '..', - 'react-devtools-shared', - 'babel.config.js', - ), - }, + options: babelOptions, }, { test: /\.css$/, diff --git a/packages/react-devtools-extensions/firefox/manifest.json b/packages/react-devtools-extensions/firefox/manifest.json index a97422cedf86d0..44327c8566f74c 100644 --- a/packages/react-devtools-extensions/firefox/manifest.json +++ b/packages/react-devtools-extensions/firefox/manifest.json @@ -32,7 +32,7 @@ "devtools_page": "main.html", - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + "content_security_policy": "script-src 'self' 'unsafe-eval' blob:; object-src 'self'", "web_accessible_resources": [ "main.html", "panel.html", diff --git a/packages/react-devtools-extensions/package.json b/packages/react-devtools-extensions/package.json index da473f7613a681..d18f205c7c8a74 100644 --- a/packages/react-devtools-extensions/package.json +++ b/packages/react-devtools-extensions/package.json @@ -37,6 +37,7 @@ "chrome-launch": "^1.1.4", "crx": "^5.0.0", "css-loader": "^1.0.1", + "file-loader": "^6.1.0", "firefox-profile": "^1.0.2", "fs-extra": "^4.0.2", "jest-fetch-mock": "^3.0.3", @@ -55,7 +56,8 @@ "web-ext": "^3.0.0", "webpack": "^4.43.0", "webpack-cli": "^3.3.11", - "webpack-dev-server": "^3.10.3" + "webpack-dev-server": "^3.10.3", + "worker-loader": "^3.0.3" }, "dependencies": { "web-ext": "^4" diff --git a/packages/react-devtools-extensions/webpack.config.js b/packages/react-devtools-extensions/webpack.config.js index 94a12ebdd1118b..45ea0c3ab767d5 100644 --- a/packages/react-devtools-extensions/webpack.config.js +++ b/packages/react-devtools-extensions/webpack.config.js @@ -19,6 +19,15 @@ const DEVTOOLS_VERSION = getVersionString(); const featureFlagTarget = process.env.FEATURE_FLAG_TARGET || 'extension-oss'; +const babelOptions = { + configFile: resolve( + __dirname, + '..', + 'react-devtools-shared', + 'babel.config.js', + ), +}; + module.exports = { mode: __DEV__ ? 'development' : 'production', devtool: __DEV__ ? 'cheap-module-eval-source-map' : false, @@ -81,17 +90,25 @@ module.exports = { ], rules: [ + { + test: /\.worker\.js$/, + use: [ + { + loader: 'worker-loader', + options: { + inline: 'no-fallback', + }, + }, + { + loader: 'babel-loader', + options: babelOptions, + }, + ], + }, { test: /\.js$/, loader: 'babel-loader', - options: { - configFile: resolve( - __dirname, - '..', - 'react-devtools-shared', - 'babel.config.js', - ), - }, + options: babelOptions, }, { test: /\.css$/, diff --git a/packages/react-devtools-inline/package.json b/packages/react-devtools-inline/package.json index a661fdf839957e..21efaf2bbd9a34 100644 --- a/packages/react-devtools-inline/package.json +++ b/packages/react-devtools-inline/package.json @@ -34,10 +34,12 @@ "babel-loader": "^8.0.4", "cross-env": "^3.1.4", "css-loader": "^1.0.1", + "file-loader": "^6.1.0", "raw-loader": "^3.1.0", "style-loader": "^0.23.1", "webpack": "^4.43.0", "webpack-cli": "^3.3.11", - "webpack-dev-server": "^3.10.3" + "webpack-dev-server": "^3.10.3", + "worker-loader": "^3.0.3" } } diff --git a/packages/react-devtools-inline/webpack.config.js b/packages/react-devtools-inline/webpack.config.js index 7011e7151c7607..0397d4358c5671 100644 --- a/packages/react-devtools-inline/webpack.config.js +++ b/packages/react-devtools-inline/webpack.config.js @@ -16,6 +16,15 @@ const __DEV__ = NODE_ENV === 'development'; const DEVTOOLS_VERSION = getVersionString(); +const babelOptions = { + configFile: resolve( + __dirname, + '..', + 'react-devtools-shared', + 'babel.config.js', + ), +}; + module.exports = { mode: __DEV__ ? 'development' : 'production', devtool: __DEV__ ? 'eval-cheap-source-map' : 'source-map', @@ -65,17 +74,25 @@ module.exports = { ], module: { rules: [ + { + test: /\.worker\.js$/, + use: [ + { + loader: 'worker-loader', + options: { + inline: 'no-fallback', + }, + }, + { + loader: 'babel-loader', + options: babelOptions, + }, + ], + }, { test: /\.js$/, loader: 'babel-loader', - options: { - configFile: resolve( - __dirname, - '..', - 'react-devtools-shared', - 'babel.config.js', - ), - }, + options: babelOptions, }, { test: /\.css$/, diff --git a/packages/react-devtools-scheduling-profiler/buildUtils.js b/packages/react-devtools-scheduling-profiler/buildUtils.js deleted file mode 100644 index b0971c48611121..00000000000000 --- a/packages/react-devtools-scheduling-profiler/buildUtils.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -const {execSync} = require('child_process'); -const {readFileSync} = require('fs'); -const {resolve} = require('path'); - -function getGitCommit() { - try { - return execSync('git show -s --format=%h') - .toString() - .trim(); - } catch (error) { - // Mozilla runs this command from a git archive. - // In that context, there is no Git revision. - return null; - } -} - -function getVersionString() { - const packageVersion = JSON.parse( - readFileSync(resolve(__dirname, './package.json')), - ).version; - - const commit = getGitCommit(); - - return `${packageVersion}-${commit}`; -} - -module.exports = { - getVersionString, -}; diff --git a/packages/react-devtools-scheduling-profiler/package.json b/packages/react-devtools-scheduling-profiler/package.json index 705261279e3049..e3b87c664fafda 100644 --- a/packages/react-devtools-scheduling-profiler/package.json +++ b/packages/react-devtools-scheduling-profiler/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "react-devtools-scheduling-profiler", - "version": "0.0.0", + "version": "4.14.0", "license": "MIT", "scripts": { "build": "cross-env NODE_ENV=production cross-env TARGET=remote webpack --config webpack.config.js", diff --git a/packages/react-devtools-scheduling-profiler/src/App.css b/packages/react-devtools-scheduling-profiler/src/App.css deleted file mode 100644 index 1ea3d75fcc595f..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/App.css +++ /dev/null @@ -1,19 +0,0 @@ -.DevTools { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - background-color: var(--color-background); - color: var(--color-text); -} - -.TabContent { - flex: 1 1 100%; - overflow: auto; - -webkit-app-region: no-drag; -} - -.DevTools, .DevTools * { - box-sizing: border-box; - -webkit-font-smoothing: var(--font-smoothing); -} diff --git a/packages/react-devtools-scheduling-profiler/src/App.js b/packages/react-devtools-scheduling-profiler/src/App.js index 9a27253b6c0326..555dd65f510db4 100644 --- a/packages/react-devtools-scheduling-profiler/src/App.js +++ b/packages/react-devtools-scheduling-profiler/src/App.js @@ -15,20 +15,9 @@ import '@reach/tooltip/styles.css'; import * as React from 'react'; import {SchedulingProfiler} from './SchedulingProfiler'; -import {useBrowserTheme, useDisplayDensity} from './hooks'; -import styles from './App.css'; import 'react-devtools-shared/src/devtools/views/root.css'; export default function App() { - useBrowserTheme(); - useDisplayDensity(); - - return ( -
-
- -
-
- ); + return ; } diff --git a/packages/react-devtools-scheduling-profiler/src/CanvasPage.js b/packages/react-devtools-scheduling-profiler/src/CanvasPage.js index 9b20cd2fb80d7c..1c3f38f266ab88 100644 --- a/packages/react-devtools-scheduling-profiler/src/CanvasPage.js +++ b/packages/react-devtools-scheduling-profiler/src/CanvasPage.js @@ -52,9 +52,9 @@ import { import {COLORS} from './content-views/constants'; import EventTooltip from './EventTooltip'; -import ContextMenu from './context/ContextMenu'; -import ContextMenuItem from './context/ContextMenuItem'; -import useContextMenu from './context/useContextMenu'; +import ContextMenu from 'react-devtools-shared/src/devtools/ContextMenu/ContextMenu'; +import ContextMenuItem from 'react-devtools-shared/src/devtools/ContextMenu/ContextMenuItem'; +import useContextMenu from 'react-devtools-shared/src/devtools/ContextMenu/useContextMenu'; import {getBatchRange} from './utils/getBatchRange'; import styles from './CanvasPage.css'; diff --git a/packages/react-devtools-scheduling-profiler/src/EventTooltip.css b/packages/react-devtools-scheduling-profiler/src/EventTooltip.css index f721295b2f2ba3..3e7cdc08c651db 100644 --- a/packages/react-devtools-scheduling-profiler/src/EventTooltip.css +++ b/packages/react-devtools-scheduling-profiler/src/EventTooltip.css @@ -6,9 +6,9 @@ padding: 0.25rem; user-select: none; pointer-events: none; - background-color: #ffffff; - border: 1px solid #ccc; - box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2); + background-color: var(--color-background); + border: 1px solid var(border); + box-shadow: 1px 1px 2px var(--color-shadow); font-size: 11px; } @@ -26,7 +26,7 @@ } .DetailsGridLabel { - color: #666; + color: var(--color-dim); text-align: right; } @@ -56,14 +56,14 @@ line-height: 1.5; -webkit-mask-image: linear-gradient( 180deg, - #fff, - #fff 5em, + var(--color-background), + var(--color-background) 5em, transparent ); mask-image: linear-gradient( 180deg, - #fff, - #fff 5em, + var(--color-background), + var(--color-background) 5em, transparent ); white-space: pre; diff --git a/packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.css b/packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.css index e16279192b83ee..b5666e6bfca1e8 100644 --- a/packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.css +++ b/packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.css @@ -1,21 +1,5 @@ -.SchedulingProfiler { - width: 100%; - height: 100%; - position: relative; - display: flex; - flex-direction: column; - font-family: var(--font-family-sans); - font-size: var(--font-size-sans-normal); - background-color: var(--color-background); - color: var(--color-text); -} - -.SchedulingProfiler, .SchedulingProfiler * { - box-sizing: border-box; - -webkit-font-smoothing: var(--font-smoothing); -} - .Content { + width: 100%; position: relative; flex: 1 1 auto; display: flex; @@ -51,58 +35,3 @@ font-size: var(--font-size-sans-large); margin-bottom: 0.5rem; } - -.Toolbar { - height: 2.25rem; - padding: 0 0.25rem; - flex: 0 0 auto; - display: flex; - align-items: center; - border-bottom: 1px solid var(--color-border); -} - -.VRule { - height: 20px; - width: 1px; - border-left: 1px solid var(--color-border); - padding-left: 0.25rem; - margin-left: 0.25rem; -} - -.Spacer { - flex: 1; -} - -.Link { - color: var(--color-button); -} - -.ScreenshotWrapper { - max-width: 30rem; - padding: 0 1rem; - margin-bottom: 2rem; -} - -.Screenshot { - width: 100%; - border-radius: 0.4em; - border: 2px solid var(--color-border); -} - -.AppName { - font-size: var(--font-size-sans-large); - margin-right: 0.5rem; - user-select: none; -} - -@media screen and (max-width: 350px) { - .AppName { - display: none; - } -} - -@media screen and (max-height: 600px) { - .ScreenshotWrapper { - display: none; - } -} \ No newline at end of file diff --git a/packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.js b/packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.js index 2f232b6bbe183a..2dfbb837de4dc6 100644 --- a/packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.js +++ b/packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.js @@ -14,13 +14,11 @@ import type {ImportWorkerOutputData} from './import-worker/import.worker'; import * as React from 'react'; import {Suspense, useCallback, useState} from 'react'; import {createResource} from 'react-devtools-shared/src/devtools/cache'; -import ReactLogo from 'react-devtools-shared/src/devtools/views/ReactLogo'; import ImportButton from './ImportButton'; import CanvasPage from './CanvasPage'; import ImportWorker from './import-worker/import.worker'; -import profilerBrowser from './assets/profilerBrowser.png'; import styles from './SchedulingProfiler.css'; type DataResource = Resource; @@ -63,39 +61,23 @@ export function SchedulingProfiler(_: {||}) { }, []); return ( -
-
- - Concurrent Mode Profiler -
- -
-
-
- {dataResource ? ( - }> - - - ) : ( - - )} -
+
+ {dataResource ? ( + }> + + + ) : ( + + )}
); } const Welcome = ({onFileSelect}: {|onFileSelect: (file: File) => void|}) => (
-
- Profiler screenshot -
Welcome!
Click the import button diff --git a/packages/react-devtools-scheduling-profiler/src/SchedulingProfilerFeatureFlags.js b/packages/react-devtools-scheduling-profiler/src/SchedulingProfilerFeatureFlags.js deleted file mode 100644 index 7558576c31781a..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/SchedulingProfilerFeatureFlags.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export const enableDarkMode = false; diff --git a/packages/react-devtools-scheduling-profiler/src/assets/logo.svg b/packages/react-devtools-scheduling-profiler/src/assets/logo.svg deleted file mode 100644 index 2e5df0d3ab2f27..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/react-devtools-scheduling-profiler/src/assets/profilerBrowser.png b/packages/react-devtools-scheduling-profiler/src/assets/profilerBrowser.png deleted file mode 100644 index b0282be2f68289..00000000000000 Binary files a/packages/react-devtools-scheduling-profiler/src/assets/profilerBrowser.png and /dev/null differ diff --git a/packages/react-devtools-scheduling-profiler/src/assets/reactlogo.svg b/packages/react-devtools-scheduling-profiler/src/assets/reactlogo.svg deleted file mode 100644 index 6b60c1042f58d9..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/assets/reactlogo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/packages/react-devtools-scheduling-profiler/src/context/ContextMenu.css b/packages/react-devtools-scheduling-profiler/src/context/ContextMenu.css deleted file mode 100644 index 60848641f4949e..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/context/ContextMenu.css +++ /dev/null @@ -1,10 +0,0 @@ -.ContextMenu { - position: absolute; - border-radius: 0.125rem; - background-color: #ffffff; - border: 1px solid #ccc; - box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2); - font-size: 11px; - overflow: hidden; - z-index: 10000002; -} diff --git a/packages/react-devtools-scheduling-profiler/src/context/ContextMenu.js b/packages/react-devtools-scheduling-profiler/src/context/ContextMenu.js deleted file mode 100644 index 8b09ef1510dcf0..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/context/ContextMenu.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import type {RegistryContextType} from './Contexts'; - -import * as React from 'react'; -import {useContext, useEffect, useLayoutEffect, useRef, useState} from 'react'; -import {createPortal} from 'react-dom'; -import {RegistryContext} from './Contexts'; - -import styles from './ContextMenu.css'; - -function repositionToFit(element: HTMLElement, pageX: number, pageY: number) { - const ownerWindow = element.ownerDocument.defaultView; - if (element !== null) { - if (pageY + element.offsetHeight >= ownerWindow.innerHeight) { - if (pageY - element.offsetHeight > 0) { - element.style.top = `${pageY - element.offsetHeight}px`; - } else { - element.style.top = '0px'; - } - } else { - element.style.top = `${pageY}px`; - } - - if (pageX + element.offsetWidth >= ownerWindow.innerWidth) { - if (pageX - element.offsetWidth > 0) { - element.style.left = `${pageX - element.offsetWidth}px`; - } else { - element.style.left = '0px'; - } - } else { - element.style.left = `${pageX}px`; - } - } -} - -const HIDDEN_STATE = { - data: null, - isVisible: false, - pageX: 0, - pageY: 0, -}; - -type Props = {| - children: (data: Object) => React$Node, - id: string, -|}; - -export default function ContextMenu({children, id}: Props) { - const {hideMenu, registerMenu} = useContext( - RegistryContext, - ); - - const [state, setState] = useState(HIDDEN_STATE); - - const bodyAccessorRef = useRef(null); - const containerRef = useRef(null); - const menuRef = useRef(null); - - useEffect(() => { - if (!bodyAccessorRef.current) { - return; - } - const ownerDocument = bodyAccessorRef.current.ownerDocument; - containerRef.current = ownerDocument.createElement('div'); - if (ownerDocument.body) { - ownerDocument.body.appendChild(containerRef.current); - } - return () => { - if (ownerDocument.body && containerRef.current) { - ownerDocument.body.removeChild(containerRef.current); - } - }; - }, [bodyAccessorRef, containerRef]); - - useEffect(() => { - const showMenuFn = ({data, pageX, pageY}) => { - setState({data, isVisible: true, pageX, pageY}); - }; - const hideMenuFn = () => setState(HIDDEN_STATE); - return registerMenu(id, showMenuFn, hideMenuFn); - }, [id]); - - useLayoutEffect(() => { - if (!state.isVisible || !containerRef.current) { - return; - } - - const menu = menuRef.current; - if (!menu) { - return; - } - - const hideUnlessContains: MouseEventHandler & - TouchEventHandler & - KeyboardEventHandler = event => { - if (event.target instanceof HTMLElement && !menu.contains(event.target)) { - hideMenu(); - } - }; - - const ownerDocument = containerRef.current.ownerDocument; - ownerDocument.addEventListener('mousedown', hideUnlessContains); - ownerDocument.addEventListener('touchstart', hideUnlessContains); - ownerDocument.addEventListener('keydown', hideUnlessContains); - - const ownerWindow = ownerDocument.defaultView; - ownerWindow.addEventListener('resize', hideMenu); - - repositionToFit(menu, state.pageX, state.pageY); - - return () => { - ownerDocument.removeEventListener('mousedown', hideUnlessContains); - ownerDocument.removeEventListener('touchstart', hideUnlessContains); - ownerDocument.removeEventListener('keydown', hideUnlessContains); - - ownerWindow.removeEventListener('resize', hideMenu); - }; - }, [state]); - - if (!state.isVisible) { - return
; - } else { - const container = containerRef.current; - if (container !== null) { - return createPortal( -
- {children(state.data)} -
, - container, - ); - } else { - return null; - } - } -} diff --git a/packages/react-devtools-scheduling-profiler/src/context/ContextMenuItem.css b/packages/react-devtools-scheduling-profiler/src/context/ContextMenuItem.css deleted file mode 100644 index 19fd8284a47cb2..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/context/ContextMenuItem.css +++ /dev/null @@ -1,20 +0,0 @@ -.ContextMenuItem { - display: flex; - align-items: center; - color: #333; - padding: 0.5rem 0.75rem; - cursor: default; - border-top: 1px solid #ccc; -} -.ContextMenuItem:first-of-type { - border-top: none; -} -.ContextMenuItem:hover, -.ContextMenuItem:focus { - outline: 0; - background-color: rgba(0, 136, 250, 0.1); -} -.ContextMenuItem:active { - background-color: #0088fa; - color: #fff; -} diff --git a/packages/react-devtools-scheduling-profiler/src/context/ContextMenuItem.js b/packages/react-devtools-scheduling-profiler/src/context/ContextMenuItem.js deleted file mode 100644 index 5750bd90cd18f8..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/context/ContextMenuItem.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import type {RegistryContextType} from './Contexts'; - -import * as React from 'react'; -import {useContext} from 'react'; -import {RegistryContext} from './Contexts'; - -import styles from './ContextMenuItem.css'; - -type Props = {| - children: React$Node, - onClick: () => void, - title: string, -|}; - -export default function ContextMenuItem({children, onClick, title}: Props) { - const {hideMenu} = useContext(RegistryContext); - - const handleClick: MouseEventHandler = event => { - onClick(); - hideMenu(); - }; - - return ( -
- {children} -
- ); -} diff --git a/packages/react-devtools-scheduling-profiler/src/context/Contexts.js b/packages/react-devtools-scheduling-profiler/src/context/Contexts.js deleted file mode 100644 index 46c742e06d0b81..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/context/Contexts.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import {createContext} from 'react'; - -export type ShowFn = ({|data: Object, pageX: number, pageY: number|}) => void; -export type HideFn = () => void; -export type OnChangeFn = boolean => void; - -const idToShowFnMap = new Map(); -const idToHideFnMap = new Map(); - -let currentHideFn: ?HideFn = null; -let currentOnChange: ?OnChangeFn = null; - -function hideMenu() { - if (typeof currentHideFn === 'function') { - currentHideFn(); - - if (typeof currentOnChange === 'function') { - currentOnChange(false); - } - } - - currentHideFn = null; - currentOnChange = null; -} - -function showMenu({ - data, - id, - onChange, - pageX, - pageY, -}: {| - data: Object, - id: string, - onChange?: OnChangeFn, - pageX: number, - pageY: number, -|}) { - const showFn = idToShowFnMap.get(id); - if (typeof showFn === 'function') { - // Prevent open menus from being left hanging. - hideMenu(); - - currentHideFn = idToHideFnMap.get(id); - showFn({data, pageX, pageY}); - - if (typeof onChange === 'function') { - currentOnChange = onChange; - onChange(true); - } - } -} - -function registerMenu(id: string, showFn: ShowFn, hideFn: HideFn) { - if (idToShowFnMap.has(id)) { - throw Error(`Context menu with id "${id}" already registered.`); - } - - idToShowFnMap.set(id, showFn); - idToHideFnMap.set(id, hideFn); - - return function unregisterMenu() { - idToShowFnMap.delete(id); - idToHideFnMap.delete(id); - }; -} - -export type RegistryContextType = {| - hideMenu: typeof hideMenu, - showMenu: typeof showMenu, - registerMenu: typeof registerMenu, -|}; - -export const RegistryContext = createContext({ - hideMenu, - showMenu, - registerMenu, -}); diff --git a/packages/react-devtools-scheduling-profiler/src/context/index.js b/packages/react-devtools-scheduling-profiler/src/context/index.js deleted file mode 100644 index c903d4f8864095..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/context/index.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import {RegistryContext} from './Contexts'; -import ContextMenu from './ContextMenu'; -import ContextMenuItem from './ContextMenuItem'; -import useContextMenu from './useContextMenu'; - -export {RegistryContext, ContextMenu, ContextMenuItem, useContextMenu}; diff --git a/packages/react-devtools-scheduling-profiler/src/context/useContextMenu.js b/packages/react-devtools-scheduling-profiler/src/context/useContextMenu.js deleted file mode 100644 index 467c138f62d873..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/context/useContextMenu.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import type {OnChangeFn, RegistryContextType} from './Contexts'; - -import {useContext, useEffect} from 'react'; -import {RegistryContext} from './Contexts'; - -export default function useContextMenu({ - data, - id, - onChange, - ref, -}: {| - data: T, - id: string, - onChange: OnChangeFn, - ref: {+current: HTMLElement | null}, -|}) { - const {showMenu} = useContext(RegistryContext); - - useEffect(() => { - if (ref.current !== null) { - const handleContextMenu = (event: MouseEvent | TouchEvent) => { - event.preventDefault(); - event.stopPropagation(); - - const pageX = - (event: any).pageX || - (event.touches && (event: any).touches[0].pageX); - const pageY = - (event: any).pageY || - (event.touches && (event: any).touches[0].pageY); - - showMenu({data, id, onChange, pageX, pageY}); - }; - - const trigger = ref.current; - trigger.addEventListener('contextmenu', handleContextMenu); - - return () => { - trigger.removeEventListener('contextmenu', handleContextMenu); - }; - } - }, [data, id, showMenu]); -} diff --git a/packages/react-devtools-scheduling-profiler/src/hooks.js b/packages/react-devtools-scheduling-profiler/src/hooks.js deleted file mode 100644 index a9692010bed2c3..00000000000000 --- a/packages/react-devtools-scheduling-profiler/src/hooks.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import { - // $FlowFixMe - unstable_createMutableSource as createMutableSource, - useLayoutEffect, - // $FlowFixMe - unstable_useMutableSource as useMutableSource, -} from 'react'; - -import { - updateDisplayDensity, - updateThemeVariables, -} from 'react-devtools-shared/src/devtools/views/Settings/SettingsContext'; -import {enableDarkMode} from './SchedulingProfilerFeatureFlags'; - -export type BrowserTheme = 'dark' | 'light'; - -const DARK_MODE_QUERY = '(prefers-color-scheme: dark)'; - -const getSnapshot = window => - window.matchMedia(DARK_MODE_QUERY).matches ? 'dark' : 'light'; - -const darkModeMutableSource = createMutableSource( - window, - () => window.matchMedia(DARK_MODE_QUERY).matches, -); - -const subscribe = (window, callback) => { - const mediaQueryList = window.matchMedia(DARK_MODE_QUERY); - mediaQueryList.addEventListener('change', callback); - return () => { - mediaQueryList.removeEventListener('change', callback); - }; -}; - -export function useBrowserTheme(): void { - const theme = useMutableSource(darkModeMutableSource, getSnapshot, subscribe); - - useLayoutEffect(() => { - const documentElements = [((document.documentElement: any): HTMLElement)]; - if (enableDarkMode) { - switch (theme) { - case 'light': - updateThemeVariables('light', documentElements); - break; - case 'dark': - updateThemeVariables('dark', documentElements); - break; - default: - throw Error(`Unsupported theme value "${theme}"`); - } - } else { - updateThemeVariables('light', documentElements); - } - }, [theme]); -} - -export function useDisplayDensity(): void { - useLayoutEffect(() => { - const documentElements = [((document.documentElement: any): HTMLElement)]; - updateDisplayDensity('comfortable', documentElements); - }, []); -} diff --git a/packages/react-devtools-scheduling-profiler/src/view-base/useCanvasInteraction.js b/packages/react-devtools-scheduling-profiler/src/view-base/useCanvasInteraction.js index b08d9bbbbb466f..3b374aebbee793 100644 --- a/packages/react-devtools-scheduling-profiler/src/view-base/useCanvasInteraction.js +++ b/packages/react-devtools-scheduling-profiler/src/view-base/useCanvasInteraction.js @@ -175,15 +175,16 @@ export function useCanvasInteraction( return false; }; - document.addEventListener('mousemove', onDocumentMouseMove); - document.addEventListener('mouseup', onDocumentMouseUp); + const ownerDocument = canvas.ownerDocument; + ownerDocument.addEventListener('mousemove', onDocumentMouseMove); + ownerDocument.addEventListener('mouseup', onDocumentMouseUp); canvas.addEventListener('mousedown', onCanvasMouseDown); canvas.addEventListener('wheel', onCanvasWheel); return () => { - document.removeEventListener('mousemove', onDocumentMouseMove); - document.removeEventListener('mouseup', onDocumentMouseUp); + ownerDocument.removeEventListener('mousemove', onDocumentMouseMove); + ownerDocument.removeEventListener('mouseup', onDocumentMouseUp); canvas.removeEventListener('mousedown', onCanvasMouseDown); canvas.removeEventListener('wheel', onCanvasWheel); diff --git a/packages/react-devtools-scheduling-profiler/webpack.config.js b/packages/react-devtools-scheduling-profiler/webpack.config.js index e30d4fda13db2f..683e4bb547b3ee 100644 --- a/packages/react-devtools-scheduling-profiler/webpack.config.js +++ b/packages/react-devtools-scheduling-profiler/webpack.config.js @@ -4,7 +4,6 @@ const {resolve} = require('path'); const {DefinePlugin} = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); -const {getVersionString} = require('./buildUtils'); const NODE_ENV = process.env.NODE_ENV; if (!NODE_ENV) { @@ -22,8 +21,6 @@ const shouldUseDevServer = TARGET === 'local'; const builtModulesDir = resolve(__dirname, '..', '..', 'build', 'node_modules'); -const DEVTOOLS_VERSION = getVersionString(); - const imageInlineSizeLimit = 10000; const babelOptions = { @@ -58,7 +55,6 @@ const config = { __PROFILE__: false, __EXPERIMENTAL__: true, __VARIANT__: false, - 'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`, }), new HtmlWebpackPlugin({ title: 'React Concurrent Mode Profiler', diff --git a/packages/react-devtools-shared/src/devtools/ContextMenu/Contexts.js b/packages/react-devtools-shared/src/devtools/ContextMenu/Contexts.js index 0d2e55106c89f5..46c742e06d0b81 100644 --- a/packages/react-devtools-shared/src/devtools/ContextMenu/Contexts.js +++ b/packages/react-devtools-shared/src/devtools/ContextMenu/Contexts.js @@ -11,33 +11,52 @@ import {createContext} from 'react'; export type ShowFn = ({|data: Object, pageX: number, pageY: number|}) => void; export type HideFn = () => void; +export type OnChangeFn = boolean => void; const idToShowFnMap = new Map(); const idToHideFnMap = new Map(); -let currentHideFn = null; +let currentHideFn: ?HideFn = null; +let currentOnChange: ?OnChangeFn = null; function hideMenu() { if (typeof currentHideFn === 'function') { currentHideFn(); + + if (typeof currentOnChange === 'function') { + currentOnChange(false); + } } + + currentHideFn = null; + currentOnChange = null; } function showMenu({ data, id, + onChange, pageX, pageY, }: {| data: Object, id: string, + onChange?: OnChangeFn, pageX: number, pageY: number, |}) { const showFn = idToShowFnMap.get(id); if (typeof showFn === 'function') { + // Prevent open menus from being left hanging. + hideMenu(); + currentHideFn = idToHideFnMap.get(id); showFn({data, pageX, pageY}); + + if (typeof onChange === 'function') { + currentOnChange = onChange; + onChange(true); + } } } @@ -56,14 +75,9 @@ function registerMenu(id: string, showFn: ShowFn, hideFn: HideFn) { } export type RegistryContextType = {| - hideMenu: () => void, - showMenu: ({| - data: Object, - id: string, - pageX: number, - pageY: number, - |}) => void, - registerMenu: (string, ShowFn, HideFn) => Function, + hideMenu: typeof hideMenu, + showMenu: typeof showMenu, + registerMenu: typeof registerMenu, |}; export const RegistryContext = createContext({ diff --git a/packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js b/packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js index 1c713bae73dd58..f61cde4b0b5357 100644 --- a/packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js +++ b/packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js @@ -10,17 +10,19 @@ import {useContext, useEffect} from 'react'; import {RegistryContext} from './Contexts'; -import type {RegistryContextType} from './Contexts'; +import type {OnChangeFn, RegistryContextType} from './Contexts'; import type {ElementRef} from 'react'; export default function useContextMenu({ data, id, + onChange, ref, }: {| data: Object, id: string, - ref: {current: ElementRef<'div'> | null}, + onChange?: OnChangeFn, + ref: {current: ElementRef<*> | null}, |}) { const {showMenu} = useContext(RegistryContext); diff --git a/packages/react-devtools-shared/src/devtools/views/Icon.js b/packages/react-devtools-shared/src/devtools/views/Icon.js index ffa297610bdf56..c9ae931f5ee74e 100644 --- a/packages/react-devtools-shared/src/devtools/views/Icon.js +++ b/packages/react-devtools-shared/src/devtools/views/Icon.js @@ -21,6 +21,7 @@ export type IconType = | 'flame-chart' | 'profiler' | 'ranked-chart' + | 'scheduling-profiler' | 'search' | 'settings' | 'store-as-global-variable' @@ -64,6 +65,9 @@ export default function Icon({className = '', type}: Props) { case 'ranked-chart': pathData = PATH_RANKED_CHART; break; + case 'scheduling-profiler': + pathData = PATH_SCHEDULING_PROFILER; + break; case 'search': pathData = PATH_SEARCH; break; @@ -136,6 +140,11 @@ const PATH_FLAME_CHART = ` const PATH_PROFILER = 'M5 9.2h3V19H5zM10.6 5h2.8v14h-2.8zm5.6 8H19v6h-2.8z'; +const PATH_SCHEDULING_PROFILER = ` + M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 + 16H5V9h14v10zm0-12H5V5h14v2zM7 11h5v5H7z +`; + const PATH_SEARCH = ` M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 diff --git a/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js b/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js index 9fda41499871e0..c41b8f4fbf5cdb 100644 --- a/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js +++ b/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js @@ -16,6 +16,7 @@ import ClearProfilingDataButton from './ClearProfilingDataButton'; import CommitFlamegraph from './CommitFlamegraph'; import CommitRanked from './CommitRanked'; import RootSelector from './RootSelector'; +import {SchedulingProfiler} from 'react-devtools-scheduling-profiler/src/SchedulingProfiler'; import RecordToggle from './RecordToggle'; import ReloadAndProfileButton from './ReloadAndProfileButton'; import ProfilingImportExportButtons from './ProfilingImportExportButtons'; @@ -42,7 +43,7 @@ function Profiler(_: {||}) { } = useContext(ProfilerContext); let view = null; - if (didRecordCommits) { + if (didRecordCommits || selectedTabID === 'scheduling-profiler') { switch (selectedTabID) { case 'flame-chart': view = ; @@ -50,6 +51,10 @@ function Profiler(_: {||}) { case 'ranked-chart': view = ; break; + case 'scheduling-profiler': + console.log('SchedulingProfiler:', SchedulingProfiler); + view = ; + break; default: break; } @@ -139,6 +144,13 @@ const tabs = [ label: 'Ranked', title: 'Ranked chart', }, + null, // Divider/separator + { + id: 'scheduling-profiler', + icon: 'scheduling-profiler', + label: 'Scheduling', + title: 'Scheduling Profiler', + }, ]; const NoProfilingData = () => ( diff --git a/packages/react-devtools-shared/src/devtools/views/TabBar.css b/packages/react-devtools-shared/src/devtools/views/TabBar.css index 57abadaecf5577..96b30c3dbf550e 100644 --- a/packages/react-devtools-shared/src/devtools/views/TabBar.css +++ b/packages/react-devtools-shared/src/devtools/views/TabBar.css @@ -85,6 +85,14 @@ .TabLabelSettings { } +.VRule { + height: 20px; + width: 1px; + border-left: 1px solid var(--color-border); + padding-left: 0.25rem; + margin-left: 0.25rem; +} + @media screen and (max-width: 525px) { .IconSizeNavigation { margin-right: 0; diff --git a/packages/react-devtools-shared/src/devtools/views/TabBar.js b/packages/react-devtools-shared/src/devtools/views/TabBar.js index 18664dae86b70e..608c660293a861 100644 --- a/packages/react-devtools-shared/src/devtools/views/TabBar.js +++ b/packages/react-devtools-shared/src/devtools/views/TabBar.js @@ -29,7 +29,7 @@ export type Props = {| disabled?: boolean, id: string, selectTab: (tabID: any) => void, - tabs: Array, + tabs: Array, type: 'navigation' | 'profiler' | 'settings', |}; @@ -41,8 +41,9 @@ export default function TabBar({ tabs, type, }: Props) { - if (!tabs.some(tab => tab.id === currentTab)) { - selectTab(tabs[0].id); + if (!tabs.some(tab => tab !== null && tab.id === currentTab)) { + const firstTab = ((tabs.find(tab => tab !== null): any): TabInfo); + selectTab(firstTab.id); } const onChange = useCallback( @@ -88,7 +89,13 @@ export default function TabBar({ return ( - {tabs.map(({icon, id, label, title}) => { + {tabs.map(tab => { + if (tab === null) { + return
; + } + + const {icon, id, label, title} = tab; + let button = (