mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[DRAFT] Import scheduling profiler into DevTools Profiler (#21897)
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
.ContextMenu {
|
||||
position: absolute;
|
||||
background-color: var(--color-context-background);
|
||||
box-shadow: 1px 1px 2px var(--color-shadow);
|
||||
border-radius: 0.25rem;
|
||||
overflow: hidden;
|
||||
z-index: 10000002;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -54,7 +54,9 @@ type Props = {|
|
||||
|};
|
||||
|
||||
export default function ContextMenu({children, id}: Props) {
|
||||
const {registerMenu} = useContext<RegistryContextType>(RegistryContext);
|
||||
const {hideMenu, registerMenu} = useContext<RegistryContextType>(
|
||||
RegistryContext,
|
||||
);
|
||||
|
||||
const [state, setState] = useState(HIDDEN_STATE);
|
||||
|
||||
@@ -75,11 +77,11 @@ export default function ContextMenu({children, id}: Props) {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const showMenu = ({data, pageX, pageY}) => {
|
||||
const showMenuFn = ({data, pageX, pageY}) => {
|
||||
setState({data, isVisible: true, pageX, pageY});
|
||||
};
|
||||
const hideMenu = () => setState(HIDDEN_STATE);
|
||||
return registerMenu(id, showMenu, hideMenu);
|
||||
const hideMenuFn = () => setState(HIDDEN_STATE);
|
||||
return registerMenu(id, showMenuFn, hideMenuFn);
|
||||
}, [id]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@@ -92,21 +94,17 @@ export default function ContextMenu({children, id}: Props) {
|
||||
if (container !== null) {
|
||||
const hideUnlessContains = event => {
|
||||
if (!menu.contains(event.target)) {
|
||||
setState(HIDDEN_STATE);
|
||||
hideMenu();
|
||||
}
|
||||
};
|
||||
|
||||
const hide = event => {
|
||||
setState(HIDDEN_STATE);
|
||||
};
|
||||
|
||||
const ownerDocument = container.ownerDocument;
|
||||
ownerDocument.addEventListener('mousedown', hideUnlessContains);
|
||||
ownerDocument.addEventListener('touchstart', hideUnlessContains);
|
||||
ownerDocument.addEventListener('keydown', hideUnlessContains);
|
||||
|
||||
const ownerWindow = ownerDocument.defaultView;
|
||||
ownerWindow.addEventListener('resize', hide);
|
||||
ownerWindow.addEventListener('resize', hideMenu);
|
||||
|
||||
repositionToFit(menu, state.pageX, state.pageY);
|
||||
|
||||
@@ -115,7 +113,7 @@ export default function ContextMenu({children, id}: Props) {
|
||||
ownerDocument.removeEventListener('touchstart', hideUnlessContains);
|
||||
ownerDocument.removeEventListener('keydown', hideUnlessContains);
|
||||
|
||||
ownerWindow.removeEventListener('resize', hide);
|
||||
ownerWindow.removeEventListener('resize', hideMenu);
|
||||
};
|
||||
}
|
||||
}, [state]);
|
||||
|
||||
@@ -11,33 +11,53 @@ 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<string, ShowFn>();
|
||||
const idToHideFnMap = new Map<string, HideFn>();
|
||||
|
||||
let currentHideFn = null;
|
||||
let currentHide: ?HideFn = null;
|
||||
let currentOnChange: ?OnChangeFn = null;
|
||||
|
||||
function hideMenu() {
|
||||
if (typeof currentHideFn === 'function') {
|
||||
currentHideFn();
|
||||
if (typeof currentHide === 'function') {
|
||||
currentHide();
|
||||
|
||||
if (typeof currentOnChange === 'function') {
|
||||
currentOnChange(false);
|
||||
}
|
||||
}
|
||||
|
||||
currentHide = 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') {
|
||||
currentHideFn = idToHideFnMap.get(id);
|
||||
// Prevent open menus from being left hanging.
|
||||
hideMenu();
|
||||
|
||||
currentHide = idToHideFnMap.get(id);
|
||||
|
||||
showFn({data, pageX, pageY});
|
||||
|
||||
if (typeof onChange === 'function') {
|
||||
currentOnChange = onChange;
|
||||
onChange(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,14 +76,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<RegistryContextType>({
|
||||
|
||||
@@ -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<RegistryContextType>(RegistryContext);
|
||||
|
||||
@@ -37,7 +39,7 @@ export default function useContextMenu({
|
||||
(event: any).pageY ||
|
||||
(event.touches && (event: any).touches[0].pageY);
|
||||
|
||||
showMenu({data, id, pageX, pageY});
|
||||
showMenu({data, id, onChange, pageX, pageY});
|
||||
};
|
||||
|
||||
const trigger = ref.current;
|
||||
|
||||
@@ -62,6 +62,7 @@ type Config = {|
|
||||
isProfiling?: boolean,
|
||||
supportsNativeInspection?: boolean,
|
||||
supportsReloadAndProfile?: boolean,
|
||||
supportsSchedulingProfiler?: boolean,
|
||||
supportsProfiling?: boolean,
|
||||
supportsTraceUpdates?: boolean,
|
||||
|};
|
||||
@@ -159,6 +160,7 @@ export default class Store extends EventEmitter<{|
|
||||
_supportsNativeInspection: boolean = true;
|
||||
_supportsProfiling: boolean = false;
|
||||
_supportsReloadAndProfile: boolean = false;
|
||||
_supportsSchedulingProfiler: boolean = false;
|
||||
_supportsTraceUpdates: boolean = false;
|
||||
|
||||
_unsupportedBridgeProtocol: BridgeProtocol | null = null;
|
||||
@@ -193,6 +195,7 @@ export default class Store extends EventEmitter<{|
|
||||
supportsNativeInspection,
|
||||
supportsProfiling,
|
||||
supportsReloadAndProfile,
|
||||
supportsSchedulingProfiler,
|
||||
supportsTraceUpdates,
|
||||
} = config;
|
||||
this._supportsNativeInspection = supportsNativeInspection !== false;
|
||||
@@ -202,6 +205,9 @@ export default class Store extends EventEmitter<{|
|
||||
if (supportsReloadAndProfile) {
|
||||
this._supportsReloadAndProfile = true;
|
||||
}
|
||||
if (supportsSchedulingProfiler) {
|
||||
this._supportsSchedulingProfiler = true;
|
||||
}
|
||||
if (supportsTraceUpdates) {
|
||||
this._supportsTraceUpdates = true;
|
||||
}
|
||||
@@ -414,6 +420,10 @@ export default class Store extends EventEmitter<{|
|
||||
);
|
||||
}
|
||||
|
||||
get supportsSchedulingProfiler(): boolean {
|
||||
return this._supportsSchedulingProfiler;
|
||||
}
|
||||
|
||||
get supportsTraceUpdates(): boolean {
|
||||
return this._supportsTraceUpdates;
|
||||
}
|
||||
|
||||
+14
-10
@@ -23,6 +23,7 @@ import useContextMenu from '../../ContextMenu/useContextMenu';
|
||||
import {meta} from '../../../hydration';
|
||||
import {getHookSourceLocationKey} from 'react-devtools-shared/src/hookNamesCache';
|
||||
import {enableProfilerChangedHookIndices} from 'react-devtools-feature-flags';
|
||||
import HookNamesContext from 'react-devtools-shared/src/devtools/views/Components/HookNamesContext';
|
||||
|
||||
import type {InspectedElement} from './types';
|
||||
import type {HooksNode, HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
|
||||
@@ -52,6 +53,8 @@ export function InspectedElementHooksTree({
|
||||
}: HooksTreeViewProps) {
|
||||
const {hooks, id} = inspectedElement;
|
||||
|
||||
const {loadHookNames: loadHookNamesFunction} = useContext(HookNamesContext);
|
||||
|
||||
// Changing parseHookNames is done in a transition, because it suspends.
|
||||
// This value is done outside of the transition, so the UI toggle feels responsive.
|
||||
const [parseHookNamesOptimistic, setParseHookNamesOptimistic] = useState(
|
||||
@@ -82,16 +85,17 @@ export function InspectedElementHooksTree({
|
||||
<div className={styles.HooksTreeView}>
|
||||
<div className={styles.HeaderRow}>
|
||||
<div className={styles.Header}>hooks</div>
|
||||
{(!parseHookNames || hookParsingFailed) && (
|
||||
<Toggle
|
||||
className={hookParsingFailed ? styles.ToggleError : null}
|
||||
isChecked={parseHookNamesOptimistic}
|
||||
isDisabled={parseHookNamesOptimistic || hookParsingFailed}
|
||||
onChange={handleChange}
|
||||
title={toggleTitle}>
|
||||
<ButtonIcon type="parse-hook-names" />
|
||||
</Toggle>
|
||||
)}
|
||||
{loadHookNamesFunction !== null &&
|
||||
(!parseHookNames || hookParsingFailed) && (
|
||||
<Toggle
|
||||
className={hookParsingFailed ? styles.ToggleError : null}
|
||||
isChecked={parseHookNamesOptimistic}
|
||||
isDisabled={parseHookNamesOptimistic || hookParsingFailed}
|
||||
onChange={handleChange}
|
||||
title={toggleTitle}>
|
||||
<ButtonIcon type="parse-hook-names" />
|
||||
</Toggle>
|
||||
)}
|
||||
<Button onClick={handleCopy} title="Copy to clipboard">
|
||||
<ButtonIcon type="copy" />
|
||||
</Button>
|
||||
|
||||
+32
-27
@@ -24,6 +24,7 @@ import {TreeContextController} from './Components/TreeContext';
|
||||
import ViewElementSourceContext from './Components/ViewElementSourceContext';
|
||||
import HookNamesContext from './Components/HookNamesContext';
|
||||
import {ProfilerContextController} from './Profiler/ProfilerContext';
|
||||
import {SchedulingProfilerContextController} from 'react-devtools-scheduling-profiler/src/SchedulingProfilerContext';
|
||||
import {ModalDialogContextController} from './ModalDialog';
|
||||
import ReactLogo from './ReactLogo';
|
||||
import UnsupportedBridgeProtocolDialog from './UnsupportedBridgeProtocolDialog';
|
||||
@@ -218,36 +219,40 @@ export default function DevTools({
|
||||
<HookNamesContext.Provider value={hookNamesContext}>
|
||||
<TreeContextController>
|
||||
<ProfilerContextController>
|
||||
<div className={styles.DevTools} ref={devToolsRef}>
|
||||
{showTabBar && (
|
||||
<div className={styles.TabBar}>
|
||||
<ReactLogo />
|
||||
<span className={styles.DevToolsVersion}>
|
||||
{process.env.DEVTOOLS_VERSION}
|
||||
</span>
|
||||
<div className={styles.Spacer} />
|
||||
<TabBar
|
||||
currentTab={tab}
|
||||
id="DevTools"
|
||||
selectTab={setTab}
|
||||
tabs={tabs}
|
||||
type="navigation"
|
||||
<SchedulingProfilerContextController>
|
||||
<div className={styles.DevTools} ref={devToolsRef}>
|
||||
{showTabBar && (
|
||||
<div className={styles.TabBar}>
|
||||
<ReactLogo />
|
||||
<span className={styles.DevToolsVersion}>
|
||||
{process.env.DEVTOOLS_VERSION}
|
||||
</span>
|
||||
<div className={styles.Spacer} />
|
||||
<TabBar
|
||||
currentTab={tab}
|
||||
id="DevTools"
|
||||
selectTab={setTab}
|
||||
tabs={tabs}
|
||||
type="navigation"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={styles.TabContent}
|
||||
hidden={tab !== 'components'}>
|
||||
<Components
|
||||
portalContainer={componentsPortalContainer}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={styles.TabContent}
|
||||
hidden={tab !== 'profiler'}>
|
||||
<Profiler
|
||||
portalContainer={profilerPortalContainer}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={styles.TabContent}
|
||||
hidden={tab !== 'components'}>
|
||||
<Components
|
||||
portalContainer={componentsPortalContainer}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={styles.TabContent}
|
||||
hidden={tab !== 'profiler'}>
|
||||
<Profiler portalContainer={profilerPortalContainer} />
|
||||
</div>
|
||||
</div>
|
||||
</SchedulingProfilerContextController>
|
||||
</ProfilerContextController>
|
||||
</TreeContextController>
|
||||
</HookNamesContext.Provider>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
@@ -26,6 +27,7 @@ import SettingsModal from 'react-devtools-shared/src/devtools/views/Settings/Set
|
||||
import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
|
||||
import {SettingsModalContextController} from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext';
|
||||
import portaledContent from '../portaledContent';
|
||||
import {StoreContext} from '../context';
|
||||
|
||||
import styles from './Profiler.css';
|
||||
|
||||
@@ -41,8 +43,12 @@ function Profiler(_: {||}) {
|
||||
supportsProfiling,
|
||||
} = useContext(ProfilerContext);
|
||||
|
||||
const {supportsSchedulingProfiler} = useContext(StoreContext);
|
||||
|
||||
let showRightColumn = true;
|
||||
|
||||
let view = null;
|
||||
if (didRecordCommits) {
|
||||
if (didRecordCommits || selectedTabID === 'scheduling-profiler') {
|
||||
switch (selectedTabID) {
|
||||
case 'flame-chart':
|
||||
view = <CommitFlamegraph />;
|
||||
@@ -50,6 +56,10 @@ function Profiler(_: {||}) {
|
||||
case 'ranked-chart':
|
||||
view = <CommitRanked />;
|
||||
break;
|
||||
case 'scheduling-profiler':
|
||||
view = <SchedulingProfiler />;
|
||||
showRightColumn = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -101,7 +111,9 @@ function Profiler(_: {||}) {
|
||||
currentTab={selectedTabID}
|
||||
id="Profiler"
|
||||
selectTab={selectTab}
|
||||
tabs={tabs}
|
||||
tabs={
|
||||
supportsSchedulingProfiler ? tabsWithSchedulingProfiler : tabs
|
||||
}
|
||||
type="profiler"
|
||||
/>
|
||||
<RootSelector />
|
||||
@@ -119,7 +131,7 @@ function Profiler(_: {||}) {
|
||||
<ModalDialog />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.RightColumn}>{sidebar}</div>
|
||||
{showRightColumn && <div className={styles.RightColumn}>{sidebar}</div>}
|
||||
<SettingsModal />
|
||||
</div>
|
||||
</SettingsModalContextController>
|
||||
@@ -141,6 +153,17 @@ const tabs = [
|
||||
},
|
||||
];
|
||||
|
||||
const tabsWithSchedulingProfiler = [
|
||||
...tabs,
|
||||
null, // Divider/separator
|
||||
{
|
||||
id: 'scheduling-profiler',
|
||||
icon: 'scheduling-profiler',
|
||||
label: 'Scheduling',
|
||||
title: 'Scheduling Profiler',
|
||||
},
|
||||
];
|
||||
|
||||
const NoProfilingData = () => (
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Header}>No profiling data has been recorded.</div>
|
||||
|
||||
+2
-1
@@ -19,7 +19,8 @@ import {StoreContext} from '../context';
|
||||
|
||||
import type {ProfilingDataFrontend} from './types';
|
||||
|
||||
export type TabID = 'flame-chart' | 'ranked-chart';
|
||||
// TODO (scheduling profiler) Should this be its own context?
|
||||
export type TabID = 'flame-chart' | 'ranked-chart' | 'scheduling-profiler';
|
||||
|
||||
export type Context = {|
|
||||
// Which tab is selected in the Profiler UI?
|
||||
|
||||
+25
-6
@@ -19,13 +19,17 @@ import {
|
||||
prepareProfilingDataFrontendFromExport,
|
||||
} from './utils';
|
||||
import {downloadFile} from '../utils';
|
||||
import {SchedulingProfilerContext} from 'react-devtools-scheduling-profiler/src/SchedulingProfilerContext';
|
||||
|
||||
import styles from './ProfilingImportExportButtons.css';
|
||||
|
||||
import type {ProfilingDataExport} from './types';
|
||||
|
||||
export default function ProfilingImportExportButtons() {
|
||||
const {isProfiling, profilingData, rootID} = useContext(ProfilerContext);
|
||||
const {isProfiling, profilingData, rootID, selectedTabID} = useContext(
|
||||
ProfilerContext,
|
||||
);
|
||||
const {importSchedulingProfilerData} = useContext(SchedulingProfilerContext);
|
||||
const store = useContext(StoreContext);
|
||||
const {profilerStore} = store;
|
||||
|
||||
@@ -64,13 +68,13 @@ export default function ProfilingImportExportButtons() {
|
||||
}
|
||||
}, [rootID, profilingData]);
|
||||
|
||||
const uploadData = useCallback(() => {
|
||||
const clickInputElement = useCallback(() => {
|
||||
if (inputRef.current !== null) {
|
||||
inputRef.current.click();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleFiles = useCallback(() => {
|
||||
const importProfilerData = useCallback(() => {
|
||||
const input = inputRef.current;
|
||||
if (input !== null && input.files.length > 0) {
|
||||
const fileReader = new FileReader();
|
||||
@@ -104,6 +108,13 @@ export default function ProfilingImportExportButtons() {
|
||||
}
|
||||
}, [modalDialogDispatch, profilerStore]);
|
||||
|
||||
const importSchedulingProfilerDataWrapper = event => {
|
||||
const input = inputRef.current;
|
||||
if (input !== null && input.files.length > 0) {
|
||||
importSchedulingProfilerData(input.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={styles.VRule} />
|
||||
@@ -111,18 +122,26 @@ export default function ProfilingImportExportButtons() {
|
||||
ref={inputRef}
|
||||
className={styles.Input}
|
||||
type="file"
|
||||
onChange={handleFiles}
|
||||
onChange={
|
||||
selectedTabID === 'scheduling-profiler'
|
||||
? importSchedulingProfilerDataWrapper
|
||||
: importProfilerData
|
||||
}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<a ref={downloadRef} className={styles.Input} />
|
||||
<Button
|
||||
disabled={isProfiling}
|
||||
onClick={uploadData}
|
||||
onClick={clickInputElement}
|
||||
title="Load profile...">
|
||||
<ButtonIcon type="import" />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isProfiling || !profilerStore.didRecordCommits}
|
||||
disabled={
|
||||
isProfiling ||
|
||||
!profilerStore.didRecordCommits ||
|
||||
selectedTabID === 'scheduling-profiler'
|
||||
}
|
||||
onClick={downloadData}
|
||||
title="Save profile...">
|
||||
<ButtonIcon type="export" />
|
||||
|
||||
+137
-1
@@ -411,11 +411,16 @@ export function updateThemeVariables(
|
||||
updateStyleHelper(theme, 'color-record-active', documentElements);
|
||||
updateStyleHelper(theme, 'color-record-hover', documentElements);
|
||||
updateStyleHelper(theme, 'color-record-inactive', documentElements);
|
||||
updateStyleHelper(theme, 'color-resize-bar', documentElements);
|
||||
updateStyleHelper(theme, 'color-color-scroll-thumb', documentElements);
|
||||
updateStyleHelper(theme, 'color-color-scroll-track', documentElements);
|
||||
updateStyleHelper(theme, 'color-search-match', documentElements);
|
||||
updateStyleHelper(theme, 'color-shadow', documentElements);
|
||||
updateStyleHelper(theme, 'color-search-match-current', documentElements);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-flame-graph-label',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-selected-tree-highlight-active',
|
||||
@@ -426,6 +431,137 @@ export function updateThemeVariables(
|
||||
'color-selected-tree-highlight-inactive',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-priority-background',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-priority-border',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-user-timing',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-user-timing-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-idle',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-idle-selected',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-idle-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-render',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-render-selected',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-render-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-commit',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-commit-selected',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-commit-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-layout-effects',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-layout-effects-selected',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-layout-effects-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-passive-effects',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-passive-effects-selected',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-passive-effects-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-schedule',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-schedule-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-schedule-cascading',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-schedule-cascading-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-suspend',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-suspend-hover',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(
|
||||
theme,
|
||||
'color-scheduling-profiler-react-work-border',
|
||||
documentElements,
|
||||
);
|
||||
updateStyleHelper(theme, 'color-shadow', documentElements);
|
||||
updateStyleHelper(theme, 'color-tab-selected-border', documentElements);
|
||||
updateStyleHelper(theme, 'color-text', documentElements);
|
||||
updateStyleHelper(theme, 'color-text-invalid', documentElements);
|
||||
|
||||
@@ -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;
|
||||
|
||||
+11
-4
@@ -29,7 +29,7 @@ export type Props = {|
|
||||
disabled?: boolean,
|
||||
id: string,
|
||||
selectTab: (tabID: any) => void,
|
||||
tabs: Array<TabInfo>,
|
||||
tabs: Array<TabInfo | null>,
|
||||
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 (
|
||||
<Fragment>
|
||||
{tabs.map(({icon, id, label, title}) => {
|
||||
{tabs.map(tab => {
|
||||
if (tab === null) {
|
||||
return <div className={styles.VRule} />;
|
||||
}
|
||||
|
||||
const {icon, id, label, title} = tab;
|
||||
|
||||
let button = (
|
||||
<label
|
||||
className={[
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
font-size: 12px;
|
||||
background-color: var(--color-tooltip-background);
|
||||
color: var(--color-tooltip-text);
|
||||
box-shadow: 1px 1px 2px var(--color-shadow);
|
||||
|
||||
/* Make sure this is above the DevTools, which are above the Overlay */
|
||||
z-index: 10000002;
|
||||
|
||||
@@ -77,6 +77,34 @@
|
||||
--light-color-record-active: #fc3a4b;
|
||||
--light-color-record-hover: #3578e5;
|
||||
--light-color-record-inactive: #0088fa;
|
||||
--light-color-resize-bar: #cccccc;
|
||||
--light-color-scheduling-profiler-flame-graph-label: #000000;
|
||||
--light-color-scheduling-profiler-priority-background: #ededf0;
|
||||
--light-color-scheduling-profiler-priority-border: #d7d7db;
|
||||
--light-color-scheduling-profiler-user-timing: #c9cacd;
|
||||
--light-color-scheduling-profiler-user-timing-hover:#93959a;
|
||||
--light-color-scheduling-profiler-react-idle: #edf6ff;
|
||||
--light-color-scheduling-profiler-react-idle-selected:#EDF6FF;
|
||||
--light-color-scheduling-profiler-react-idle-hover:#EDF6FF;
|
||||
--light-color-scheduling-profiler-react-render: #9fc3f3;
|
||||
--light-color-scheduling-profiler-react-render-selected:#64A9F5;
|
||||
--light-color-scheduling-profiler-react-render-hover:#2683E2;
|
||||
--light-color-scheduling-profiler-react-commit: #ff718e;
|
||||
--light-color-scheduling-profiler-react-commit-selected:#FF5277;
|
||||
--light-color-scheduling-profiler-react-commit-hover:#ed0030;
|
||||
--light-color-scheduling-profiler-react-layout-effects:#c88ff0;
|
||||
--light-color-scheduling-profiler-react-layout-effects-selected:#934FC1;
|
||||
--light-color-scheduling-profiler-react-layout-effects-hover:#601593;
|
||||
--light-color-scheduling-profiler-react-passive-effects:#c88ff0;
|
||||
--light-color-scheduling-profiler-react-passive-effects-selected:#934FC1;
|
||||
--light-color-scheduling-profiler-react-passive-effects-hover:#601593;
|
||||
--light-color-scheduling-profiler-react-schedule: #9fc3f3;
|
||||
--light-color-scheduling-profiler-react-schedule-hover:#2683E2;
|
||||
--light-color-scheduling-profiler-react-schedule-cascading:#ff718e;
|
||||
--light-color-scheduling-profiler-react-schedule-cascading-hover:#ed0030;
|
||||
--light-color-scheduling-profiler-react-suspend: #a6e59f;
|
||||
--light-color-scheduling-profiler-react-suspend-hover:#13bc00;
|
||||
--light-color-scheduling-profiler-react-work-border:#ffffff;
|
||||
--light-color-scroll-thumb: #c2c2c2;
|
||||
--light-color-scroll-track: #fafafa;
|
||||
--light-color-search-match: yellow;
|
||||
@@ -146,7 +174,7 @@
|
||||
--dark-color-console-warning-border: #665500;
|
||||
--dark-color-console-warning-icon: #f4bd00;
|
||||
--dark-color-console-warning-text: #f5f2ed;
|
||||
--dark-color-context-background: rgba(255,255,255,.9);
|
||||
--dark-color-context-background: rgba(255,255,255,.95);
|
||||
--dark-color-context-background-hover: rgba(0, 136, 250, 0.1);
|
||||
--dark-color-context-background-selected: #0088fa;
|
||||
--dark-color-context-border: #eeeeee;
|
||||
@@ -169,6 +197,34 @@
|
||||
--dark-color-record-active: #fc3a4b;
|
||||
--dark-color-record-hover: #a2e9fc;
|
||||
--dark-color-record-inactive: #61dafb;
|
||||
--dark-color-resize-bar: #3d424a;
|
||||
--dark-color-scheduling-profiler-flame-graph-label: #000000;
|
||||
--dark-color-scheduling-profiler-priority-background: #1d2129;
|
||||
--dark-color-scheduling-profiler-priority-border: #282c34;
|
||||
--dark-color-scheduling-profiler-user-timing: #c9cacd;
|
||||
--dark-color-scheduling-profiler-user-timing-hover:#93959a;
|
||||
--dark-color-scheduling-profiler-react-idle: #3d485b;
|
||||
--dark-color-scheduling-profiler-react-idle-selected:#465269;
|
||||
--dark-color-scheduling-profiler-react-idle-hover:#465269;
|
||||
--dark-color-scheduling-profiler-react-render: #9fc3f3;
|
||||
--dark-color-scheduling-profiler-react-render-selected:#64A9F5;
|
||||
--dark-color-scheduling-profiler-react-render-hover:#2683E2;
|
||||
--dark-color-scheduling-profiler-react-commit: #ff718e;
|
||||
--dark-color-scheduling-profiler-react-commit-selected:#FF5277;
|
||||
--dark-color-scheduling-profiler-react-commit-hover:#ed0030;
|
||||
--dark-color-scheduling-profiler-react-layout-effects:#c88ff0;
|
||||
--dark-color-scheduling-profiler-react-layout-effects-selected:#934FC1;
|
||||
--dark-color-scheduling-profiler-react-layout-effects-hover:#601593;
|
||||
--dark-color-scheduling-profiler-react-passive-effects:#c88ff0;
|
||||
--dark-color-scheduling-profiler-react-passive-effects-selected:#934FC1;
|
||||
--dark-color-scheduling-profiler-react-passive-effects-hover:#601593;
|
||||
--dark-color-scheduling-profiler-react-schedule: #9fc3f3;
|
||||
--dark-color-scheduling-profiler-react-schedule-hover:#2683E2;
|
||||
--dark-color-scheduling-profiler-react-schedule-cascading:#ff718e;
|
||||
--dark-color-scheduling-profiler-react-schedule-cascading-hover:#ed0030;
|
||||
--dark-color-scheduling-profiler-react-suspend: #a6e59f;
|
||||
--dark-color-scheduling-profiler-react-suspend-hover:#13bc00;
|
||||
--dark-color-scheduling-profiler-react-work-border:#ffffff;
|
||||
--dark-color-scroll-thumb: #afb3b9;
|
||||
--dark-color-scroll-track: #313640;
|
||||
--dark-color-search-match: yellow;
|
||||
@@ -184,7 +240,7 @@
|
||||
--dark-color-toggle-background-on: #178fb9;
|
||||
--dark-color-toggle-background-off: #777d88;
|
||||
--dark-color-toggle-text: #ffffff;
|
||||
--dark-color-tooltip-background: rgba(255, 255, 255, 0.9);
|
||||
--dark-color-tooltip-background: rgba(255, 255, 255, 0.95);
|
||||
--dark-color-tooltip-text: #000000;
|
||||
|
||||
/* Font smoothing */
|
||||
|
||||
Reference in New Issue
Block a user