Scheduling Profiler: Redesign with DevTools styling (#19707)

Co-authored-by: Brian Vaughn <bvaughn@fb.com>
This commit is contained in:
E-Liang Tan 2020-09-04 00:08:40 +08:00 committed by GitHub
parent bcc0aa4633
commit 38a512acad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 577 additions and 311 deletions

View File

@ -1,3 +1,10 @@
/**
* 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');

View File

@ -0,0 +1,36 @@
/**
* 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,
};

View File

@ -1,7 +1,7 @@
{
"private": true,
"name": "react-devtools-scheduling-profiler",
"version": "0.0.1",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"build": "cross-env NODE_ENV=production cross-env TARGET=remote webpack --config webpack.config.js",
@ -18,6 +18,8 @@
},
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.1",
"@reach/menu-button": "^0.11.2",
"@reach/tooltip": "^0.11.2",
"babel-loader": "^8.1.0",
"css-loader": "^4.2.1",
"file-loader": "^6.0.0",

View File

@ -0,0 +1,19 @@
.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);
}

View File

@ -7,22 +7,30 @@
* @flow
*/
import type {ReactProfilerData} from './types';
// Reach styles need to come before any component styles.
// This makes overriding the styles simpler.
import '@reach/menu-button/styles.css';
import '@reach/tooltip/styles.css';
import * as React from 'react';
import {useState} from 'react';
import ImportPage from './ImportPage';
import CanvasPage from './CanvasPage';
import {ModalDialogContextController} from 'react-devtools-shared/src/devtools/views/ModalDialog';
import {SchedulingProfiler} from './SchedulingProfiler';
import {useBrowserTheme} from './hooks';
import styles from './App.css';
import 'react-devtools-shared/src/devtools/views/root.css';
export default function App() {
const [profilerData, setProfilerData] = useState<ReactProfilerData | null>(
null,
);
useBrowserTheme();
if (profilerData) {
return <CanvasPage profilerData={profilerData} />;
} else {
return <ImportPage onDataImported={setProfilerData} />;
}
return (
<ModalDialogContextController>
<div className={styles.DevTools}>
<div className={styles.TabContent}>
<SchedulingProfiler />
</div>
</div>
</ModalDialogContextController>
);
}

View File

@ -52,18 +52,15 @@ import {
import {COLORS} from './content-views/constants';
import EventTooltip from './EventTooltip';
import {ContextMenu, ContextMenuItem, useContextMenu} from './context';
import ContextMenu from './context/ContextMenu';
import ContextMenuItem from './context/ContextMenuItem';
import useContextMenu from './context/useContextMenu';
import {getBatchRange} from './utils/getBatchRange';
import styles from './CanvasPage.css';
const CONTEXT_MENU_ID = 'canvas';
type ContextMenuContextData = {|
data: ReactProfilerData,
hoveredEvent: ReactHoverContextInfo | null,
|};
type Props = {|
profilerData: ReactProfilerData,
|};
@ -284,7 +281,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
useCanvasInteraction(canvasRef, interactor);
useContextMenu<ContextMenuContextData>({
useContextMenu({
data: {
data,
hoveredEvent,
@ -357,7 +354,10 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
}
});
}
}, [hoveredEvent]);
}, [
hoveredEvent,
data, // Attach onHover callbacks when views are re-created on data change
]);
useLayoutEffect(() => {
const {current: userTimingMarksView} = userTimingMarksViewRef;
@ -396,7 +396,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
<Fragment>
<canvas ref={canvasRef} height={height} width={width} />
<ContextMenu id={CONTEXT_MENU_ID}>
{(contextData: ContextMenuContextData) => {
{contextData => {
if (contextData.hoveredEvent == null) {
return null;
}

View File

@ -0,0 +1,17 @@
/**
* https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications
*/
.Input {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
.ErrorMessage {
margin: 0.5rem 0;
color: var(--color-dim);
font-family: var(--font-family-monospace);
font-size: var(--font-size-monospace-normal);
}

View File

@ -0,0 +1,86 @@
/**
* 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 {TimelineEvent} from '@elg/speedscope';
import type {ReactProfilerData} from './types';
import * as React from 'react';
import {useCallback, useContext, useRef} from 'react';
import Button from 'react-devtools-shared/src/devtools/views/Button';
import ButtonIcon from 'react-devtools-shared/src/devtools/views/ButtonIcon';
import {ModalDialogContext} from 'react-devtools-shared/src/devtools/views/ModalDialog';
import preprocessData from './utils/preprocessData';
import {readInputData} from './utils/readInputData';
import styles from './ImportButton.css';
type Props = {|
onDataImported: (profilerData: ReactProfilerData) => void,
|};
export default function ImportButton({onDataImported}: Props) {
const inputRef = useRef<HTMLInputElement | null>(null);
const {dispatch: modalDialogDispatch} = useContext(ModalDialogContext);
const handleFiles = useCallback(async () => {
const input = inputRef.current;
if (input === null) {
return;
}
if (input.files.length > 0) {
try {
const readFile = await readInputData(input.files[0]);
const events: TimelineEvent[] = JSON.parse(readFile);
if (events.length > 0) {
onDataImported(preprocessData(events));
}
} catch (error) {
modalDialogDispatch({
type: 'SHOW',
title: 'Import failed',
content: (
<>
<div>The profiling data you selected cannot be imported.</div>
{error !== null && (
<div className={styles.ErrorMessage}>{error.message}</div>
)}
</>
),
});
}
}
// Reset input element to allow the same file to be re-imported
input.value = '';
}, [onDataImported, modalDialogDispatch]);
const uploadData = useCallback(() => {
if (inputRef.current !== null) {
inputRef.current.click();
}
}, []);
return (
<>
<input
ref={inputRef}
className={styles.Input}
type="file"
onChange={handleFiles}
tabIndex={-1}
/>
<Button onClick={uploadData} title="Load profile...">
<ButtonIcon type="import" />
</Button>
</>
);
}

View File

@ -1,163 +0,0 @@
.App {
min-height: 100vh;
background-color: #282c34;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
padding: 2rem;
}
.Card {
display: flex;
flex: 0 1 1000px;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.Card {
background-color: white;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
border-radius: 5px;
transition: 0.3s;
}
.Card:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
.Row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.Column {
margin: 2rem;
margin-right: 0;
display: flex;
flex-direction: column;
flex-basis: 100%;
flex: 1;
}
.Column:last-of-type {
margin-right: 2rem;
}
.Link {
color: #2683E2;
transition: 0.2s;
}
.Link:hover {
color: #1572D1;
}
kbd {
display: inline-block;
padding: 0 0.5em;
border: 1px solid #d7dfe4;
margin: 0 0.2em;
background-color: #f6f6f6;
border-radius: 0.2em;
}
.Screenshot {
width: 35rem;
max-width: 100%;
min-width: 25rem;
align-self: center;
justify-content: center;
border: 1px solid #d7dfe4;
border-radius: 0.4em;
box-shadow: 0 2px 4px #ddd;
}
.Header {
margin-top: 0px;
}
.LegendKey {
margin: 0;
margin-bottom: 1.25rem;
list-style: none;
padding: 0;
}
.LegendItem {
display: flex;
align-items: center;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.Buttons {
float: left; /* Float the buttons side by side */
}
.ImportButton,
.ViewSourceButton {
width: 10rem;
font-size: 1.5rem;
padding: 1.5rem;
text-align: center;
transition: all 0.3s;
cursor: pointer;
}
.ImportButton {
background-color: #2683E2;
border: none;
color: #ffffff;
}
.ImportButton:hover {
background-color: #1572D1;
}
.ImportButtonFile {
display: none;
}
.ViewSourceButton {
background-color: transparent;
color: black;
border: none;
}
.ViewSourceButton span {
cursor: pointer;
display: inline-block;
position: relative;
transition: 0.3s;
}
.ViewSourceButton span:after {
content: '\00bb';
position: absolute;
opacity: 0;
top: 0;
right: -20px;
transition: 0.3s;
}
.ViewSourceButton:hover {
background-color: white;
color: black;
}
.ViewSourceButton:hover span {
padding-right: 25px;
}
.ViewSourceButton:hover span:after {
opacity: 1;
right: 0;
}

View File

@ -1,116 +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 {TimelineEvent} from '@elg/speedscope';
import type {ReactProfilerData} from './types';
import * as React from 'react';
import {useCallback, useRef} from 'react';
import profilerBrowser from './assets/profilerBrowser.png';
import style from './ImportPage.css';
import preprocessData from './utils/preprocessData';
import {readInputData} from './utils/readInputData';
type Props = {|
onDataImported: (profilerData: ReactProfilerData) => void,
|};
export default function ImportPage({onDataImported}: Props) {
const processTimeline = useCallback(
(events: TimelineEvent[]) => {
if (events.length > 0) {
onDataImported(preprocessData(events));
}
},
[onDataImported],
);
const handleProfilerInput = useCallback(
async (event: SyntheticInputEvent<HTMLInputElement>) => {
const readFile = await readInputData(event.target.files[0]);
processTimeline(JSON.parse(readFile));
},
[processTimeline],
);
const upload = useRef(null);
return (
<div className={style.App}>
<div className={style.Card}>
<div className={style.Row}>
<div className={style.Column}>
<img
src={profilerBrowser}
className={style.Screenshot}
alt="logo"
/>
</div>
<div className={style.Column}>
<h2 className={style.Header}>React Concurrent Mode Profiler</h2>
<p>
Import a captured{' '}
<a
className={style.Link}
href="https://developers.google.com/web/tools/chrome-devtools/evaluate-performance">
performance profile
</a>{' '}
from Chrome Devtools. To zoom, scroll while holding down{' '}
<kbd>Ctrl</kbd> or <kbd>Shift</kbd>
</p>
<ul className={style.LegendKey}>
<li className={style.LegendItem}>
<svg height="20" width="20">
<circle cx="10" cy="10" r="5" fill="#ff718e" />
</svg>
State Update Scheduled
</li>
<li className={style.LegendItem}>
<svg height="20" width="20">
<circle cx="10" cy="10" r="5" fill="#9fc3f3" />
</svg>
State Update Scheduled
</li>
<li className={style.LegendItem}>
<svg height="20" width="20">
<circle cx="10" cy="10" r="5" fill="#a6e59f" />
</svg>
Suspended
</li>
</ul>
<div className={style.Buttons}>
<label htmlFor="upload">
<button
className={style.ImportButton}
onClick={() => upload.current && upload.current.click()}>
Import
</button>
<input
type="file"
ref={upload}
className={style.ImportButtonFile}
onChange={handleProfilerInput}
accept="application/json"
/>
</label>
<a href="https://github.com/facebook/react/tree/master/packages/react-devtools-scheduling-profiler">
<button className={style.ViewSourceButton}>
<span>Source </span>
</button>
</a>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,101 @@
.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 {
position: relative;
flex: 1 1 auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.Paragraph {
text-align: center;
}
.Row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex-flow: wrap;
}
.EmptyStateContainer {
text-align: center;
}
.Header {
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;
}
}

View File

@ -0,0 +1,72 @@
/**
* 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 {ReactProfilerData} from './types';
import * as React from 'react';
import {useState} from 'react';
import ImportButton from './ImportButton';
import {ModalDialog} from 'react-devtools-shared/src/devtools/views/ModalDialog';
import ReactLogo from 'react-devtools-shared/src/devtools/views/ReactLogo';
import CanvasPage from './CanvasPage';
import profilerBrowser from './assets/profilerBrowser.png';
import styles from './SchedulingProfiler.css';
export function SchedulingProfiler(_: {||}) {
const [profilerData, setProfilerData] = useState<ReactProfilerData | null>(
null,
);
const view = profilerData ? (
<CanvasPage profilerData={profilerData} />
) : (
<Welcome onDataImported={setProfilerData} />
);
return (
<div className={styles.SchedulingProfiler}>
<div className={styles.Toolbar}>
<ReactLogo />
<span className={styles.AppName}>Concurrent Mode Profiler</span>
<div className={styles.VRule} />
<ImportButton onDataImported={setProfilerData} />
<div className={styles.Spacer} />
</div>
<div className={styles.Content}>
{view}
<ModalDialog />
</div>
</div>
);
}
type WelcomeProps = {|
onDataImported: (profilerData: ReactProfilerData) => void,
|};
const Welcome = ({onDataImported}: WelcomeProps) => (
<div className={styles.EmptyStateContainer}>
<div className={styles.ScreenshotWrapper}>
<img
src={profilerBrowser}
className={styles.Screenshot}
alt="Profiler screenshot"
/>
</div>
<div className={styles.Header}>Welcome!</div>
<div className={styles.Row}>
Click the import button
<ImportButton onDataImported={onDataImported} /> to import a Chrome
performance profile.
</div>
</div>
);

View File

@ -0,0 +1,10 @@
/**
* 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;

View File

@ -6,6 +6,10 @@
*
* @flow
*/
// App constants
export {
COMFORTABLE_LINE_HEIGHT,
COMPACT_LINE_HEIGHT,
} from 'react-devtools-shared/src/constants.js';
export const REACT_TOTAL_NUM_LANES = 31;

View File

@ -0,0 +1,59 @@
/**
* 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 {
unstable_createMutableSource as createMutableSource,
unstable_useMutableSource as useMutableSource,
useLayoutEffect,
} from 'react';
import {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]);
}

View File

@ -1,13 +1,21 @@
html {
height: 100%;
}
body {
height: 100%;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: var(--font-family-sans);
font-size: var(--font-size-sans-normal);
background-color: var(--color-background);
color: var(--color-text);
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.Container {
height: 100%;
}

View File

@ -15,9 +15,10 @@ import {unstable_createRoot as createRoot} from 'react-dom';
import nullthrows from 'nullthrows';
import App from './App';
import './index.css';
import styles from './index.css';
const container = document.createElement('div');
container.className = styles.Container;
container.id = 'root';
const body = nullthrows(document.body, 'Expect document.body to exist');

View File

@ -4,6 +4,7 @@ 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) {
@ -21,6 +22,8 @@ const shouldUseDevServer = TARGET === 'local';
const builtModulesDir = resolve(__dirname, '..', '..', 'build', 'node_modules');
const DEVTOOLS_VERSION = getVersionString();
const imageInlineSizeLimit = 10000;
const config = {
@ -41,6 +44,7 @@ const config = {
__DEV__,
__PROFILE__: false,
__EXPERIMENTAL__: true,
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
}),
new HtmlWebpackPlugin({
title: 'React Concurrent Mode Profiler',

View File

@ -248,7 +248,7 @@ function updateDisplayDensity(
root.style.fontSize = fontSize;
}
function updateThemeVariables(
export function updateThemeVariables(
theme: Theme,
documentElements: DocumentElements,
): void {

115
yarn.lock
View File

@ -1833,6 +1833,14 @@
schema-utils "^2.6.5"
source-map "^0.7.3"
"@reach/auto-id@0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@reach/auto-id/-/auto-id-0.11.2.tgz#c66a905c5401d1ac3da8d26165b8d27d6e778fa6"
integrity sha512-YZ21b0Kb88wJ0t7QjSznWOYskARQMnmXY9Y2XZ5RyYcZ2krT4s3+ghghpfaPs6BKcrZDonZCrU65OFDJPa1jAw==
dependencies:
"@reach/utils" "0.11.2"
tslib "^2.0.0"
"@reach/auto-id@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@reach/auto-id/-/auto-id-0.2.0.tgz#97f9e48fe736aa5c6f4f32cf73c1f19d005f8550"
@ -1843,6 +1851,14 @@
resolved "https://registry.yarnpkg.com/@reach/component-component/-/component-component-0.1.3.tgz#5d156319572dc38995b246f81878bc2577c517e5"
integrity sha512-a1USH7L3bEfDdPN4iNZGvMEFuBfkdG+QNybeyDv8RloVFgZYRoM+KGXyy2KOfEnTUM8QWDRSROwaL3+ts5Angg==
"@reach/descendants@0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@reach/descendants/-/descendants-0.11.2.tgz#49ea1b5eb91aba8ae6dce57f6575c38aff1f9756"
integrity sha512-63Wdx32/RyjGRJc4UZKK7F1sIrb6jeGkDwvQH0hv0lRAhEjsiSQ1t2JTYDml3testFP48J0B2xS7JzNeY0zoQw==
dependencies:
"@reach/utils" "0.11.2"
tslib "^2.0.0"
"@reach/menu-button@^0.1.17":
version "0.1.18"
resolved "https://registry.yarnpkg.com/@reach/menu-button/-/menu-button-0.1.18.tgz#cb9e3bf1c2a2bdb5d618697b87ad353dfbca123e"
@ -1855,11 +1871,47 @@
"@reach/window-size" "^0.1.4"
warning "^4.0.2"
"@reach/menu-button@^0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@reach/menu-button/-/menu-button-0.11.2.tgz#7c1a955431b6b5c25e77eeaceac347bfc728bee9"
integrity sha512-8fU8Gp7wBBhMBFSGxuF62HSdjgaib+NXEM+MhOIYcAacDnP8cxkpvag/Nm/7cMdV/O5JRuuZBralG854AxaO2g==
dependencies:
"@reach/auto-id" "0.11.2"
"@reach/descendants" "0.11.2"
"@reach/popover" "0.11.2"
"@reach/utils" "0.11.2"
prop-types "^15.7.2"
tslib "^2.0.0"
"@reach/observe-rect@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.2.0.tgz#d7a6013b8aafcc64c778a0ccb83355a11204d3b2"
integrity sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==
"@reach/observe-rect@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.0.3.tgz#2ea3dcc369ab22bd9f050a92ea319321356a61e8"
integrity sha1-LqPcw2mrIr2fBQqS6jGTITVqYeg=
"@reach/popover@0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@reach/popover/-/popover-0.11.2.tgz#d7d14d8d74b0a376e31dd9a3223d16802360e0d3"
integrity sha512-fB5lScg7Sfu3k0c+bjX7QbnFOJ1nsCWYGvrH1M9qlQolWM6gSJcD+ANnEHIb/wF85zuxzgdxZY5bjTj0RZc6Lg==
dependencies:
"@reach/portal" "0.11.2"
"@reach/rect" "0.11.2"
"@reach/utils" "0.11.2"
tabbable "^4.0.0"
tslib "^2.0.0"
"@reach/portal@0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.11.2.tgz#19a671be9ff010a345892b81e710cb6e4d9f9762"
integrity sha512-/53A/rY5oX2Y7D5TpvsP+V5cSd+4MPY6f21mAmVn4DCVwpkCFOlJ059ZL7ixS85M0Jz48YQnnvBJUqwkxqUG/g==
dependencies:
"@reach/utils" "0.11.2"
tslib "^2.0.0"
"@reach/portal@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.2.1.tgz#07720b999e0063a9e179c14dbdc60fd991cfc9fa"
@ -1867,6 +1919,16 @@
dependencies:
"@reach/component-component" "^0.1.3"
"@reach/rect@0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@reach/rect/-/rect-0.11.2.tgz#bc92f97f87e81d1307054fd41c40874d3fbf5491"
integrity sha512-eoUWayAADi1ITtrc+8jN9NsBTUkfpORkOs5bQb4RnR6UA/3zlxo5VPuxWgWAG0BCohZlqsxg7NpvckNAyaiAAQ==
dependencies:
"@reach/observe-rect" "1.2.0"
"@reach/utils" "0.11.2"
prop-types "^15.7.2"
tslib "^2.0.0"
"@reach/rect@0.2.1", "@reach/rect@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@reach/rect/-/rect-0.2.1.tgz#7343020174c90e2290b844d17c03fd9c78e6b601"
@ -1875,6 +1937,19 @@
"@reach/component-component" "^0.1.3"
"@reach/observe-rect" "^1.0.3"
"@reach/tooltip@^0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@reach/tooltip/-/tooltip-0.11.2.tgz#db5c11fa72995801baac157db46f5bb6cacd494b"
integrity sha512-aTi3aLkRZMHrNiHt84vnyTWNj84rPdGYkxAfaFpxgkaKpos3XmaOPiR+n/3YzQUoJXISuw8mZezcrDtsSpr3aA==
dependencies:
"@reach/auto-id" "0.11.2"
"@reach/portal" "0.11.2"
"@reach/rect" "0.11.2"
"@reach/utils" "0.11.2"
"@reach/visually-hidden" "0.11.1"
prop-types "^15.7.2"
tslib "^2.0.0"
"@reach/tooltip@^0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@reach/tooltip/-/tooltip-0.2.2.tgz#a861ce38269b586597ab40417323b33d3d6dc927"
@ -1887,11 +1962,27 @@
"@reach/visually-hidden" "^0.1.4"
prop-types "^15.7.2"
"@reach/utils@0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.11.2.tgz#be1f03650db56fd67a16d3fc70e5262cdb139cec"
integrity sha512-fBTolYj+rKTROXmf0zHO0rCWSvw7J0ALmYj5QxW4DmITMOH5uyRuWDWOfqohIGFbOtF/sum50WTB3tvx76d+Aw==
dependencies:
"@types/warning" "^3.0.0"
tslib "^2.0.0"
warning "^4.0.3"
"@reach/utils@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.2.3.tgz#820f6a6af4301b4c5065cfc04bb89e6a3d1d723f"
integrity sha512-zM9rA8jDchr05giMhL95dPeYkK67cBQnIhCVrOKKqgWGsv+2GE/HZqeptvU4zqs0BvIqsThwov+YxVNVh5csTQ==
"@reach/visually-hidden@0.11.1":
version "0.11.1"
resolved "https://registry.yarnpkg.com/@reach/visually-hidden/-/visually-hidden-0.11.1.tgz#3d7a5742acf18372f35b9e216680edd8073f35e3"
integrity sha512-TZZNSttor2jG6ZPGI35s/8G0FNSz49QrJIhAZbnUqHyPf3+jtNqAC0dOWW8Nuk+mApDDDVYd2KZ9P2vnzllNnQ==
dependencies:
tslib "^2.0.0"
"@reach/visually-hidden@^0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@reach/visually-hidden/-/visually-hidden-0.1.4.tgz#0dc4ecedf523004337214187db70a46183bd945b"
@ -2067,6 +2158,11 @@
dependencies:
source-map "^0.6.1"
"@types/warning@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=
"@types/webpack-sources@*":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-1.4.2.tgz#5d3d4dea04008a779a90135ff96fb5c0c9e6292c"
@ -12695,6 +12791,11 @@ symbol-tree@^3.2.2, symbol-tree@^3.2.4:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
tabbable@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-4.0.0.tgz#5bff1d1135df1482cf0f0206434f15eadbeb9261"
integrity sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==
table@^5.2.3:
version "5.4.6"
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
@ -13060,7 +13161,7 @@ truncate-utf8-bytes@^1.0.0:
dependencies:
utf8-byte-length "^1.0.1"
tslib@^1.10.0, tslib@^1.9.0:
tslib@^1.10.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
@ -13070,6 +13171,16 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
tslib@^1.9.0:
version "1.11.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.2.tgz#9c79d83272c9a7aaf166f73915c9667ecdde3cc9"
integrity sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg==
tslib@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
@ -13565,7 +13676,7 @@ walker@^1.0.7, walker@~1.0.5:
dependencies:
makeerror "1.0.x"
warning@^4.0.2:
warning@^4.0.2, warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==