diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a688bb6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "all", + "printWidth": 80, + "tabWidth": 2, + "singleQuote": true +} diff --git a/package.json b/package.json index a9443f6..6b4482a 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,19 @@ "dependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", + "@mui/icons-material": "^5.6.2", "@mui/material": "^5.0.6", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", + "chroma-js": "^2.4.2", + "cors-anywhere": "^0.4.4", + "hoist-non-react-statics": "^3.3.2", + "lodash": "^4.17.21", + "prettier": "^2.6.2", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-dropzone": "^14.2.0", "react-scripts": "4.0.3", "web-vitals": "^1.0.1" }, diff --git a/public/favicon.ico b/public/favicon.ico index a11777c..e652531 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html index e18ed22..e874064 100644 --- a/public/index.html +++ b/public/index.html @@ -25,7 +25,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + Hasty Demo App diff --git a/src/App.js b/src/App.js index a629449..4a578e8 100644 --- a/src/App.js +++ b/src/App.js @@ -1,177 +1,397 @@ -import logo from "./logo.svg"; -import "./App.css"; -import { useState } from "react"; -import { CardMediaWithAnnotations } from "./ImageWithAnnotations"; +import { useEffect, useRef, useState } from 'react'; +import isEqual from 'lodash/isEqual'; +import './App.css'; +import { CardMediaWithAnnotations } from './components/imageMediaWithAnnotations/ImageWithAnnotations'; import { - AppBar, Box, Button, + CircularProgress, + Container, + Grid, Stack, - TextField, ThemeProvider, Toolbar, Typography, -} from "@mui/material"; -import { theme } from "./theme"; - -import { ReactComponent as Logo } from "./icons/hog.svg"; -import { getImageSize, readAsArrayBuffer, readAsDataURL } from "./helpers"; - -const API_BASE_URL = `http://localhost:8080/https://api.hasty.ai`; -const API_KEY = ""; -const PROJECT_ID = ""; - -function App() { - const [key, setKey] = useState(API_KEY); - const [projectId, setProjectId] = useState(PROJECT_ID); +} from '@mui/material'; +import { theme } from './theme'; +import { ReactComponent as Logo } from './img/hog.svg'; +import { getImageSize, readAsArrayBuffer, readAsDataURL } from './helpers'; +import { + API_BASE_URL, + HEADERS, + MODEL_STATUS, + PROJECT_ID, + PROXY, +} from './constants'; +import { LabelClasses } from './components/labelClasses/LabelClasses'; +import BottomLeftPattern from './img/bottomLeftPattern.svg'; +import Flywheel from './img/flywheel.svg'; +import Success from './img/success.svg'; +import RightPattern from './img/rightPattern.svg'; +import { UploadBox } from './components/uploadBox/UploadBox'; - const [modelStatus, setModelStatus] = useState("Unknown"); +export const App = () => { + const [labelClasses, setLabelClasses] = useState([]); + const [modelStatus, setModelStatus] = useState(MODEL_STATUS.UNKNOWN); const [modelId, setModelId] = useState(null); const [imageUrl, setImageUrl] = useState(null); const [imageSizes, setImageSizes] = useState({ width: 0, height: 0 }); - const [labels, setLables] = useState([]); + const [labels, setLabels] = useState([]); + const [model, setModel] = useState('instance_segmentor'); + const [imageUploading, setImageUploading] = useState(false); + const [showPredictions, setShowPredictions] = useState(false); + const [imageName, setImageName] = useState(''); + const [uploadId, setUploadId] = useState(); + const [threshold, setThreshold] = useState(50); + const [confidence, setConfidence] = useState(50); + const [predictionsLoading, setPredictionsLoading] = useState(false); + const [lastValues, setLastValues] = useState({ + confidence: 50, + threshold: 50, + }); + + const intervalRef = useRef(); + const inputRef = useRef(); - const headers = { - "X-Api-Key": key, - "content-type": "application/json", + const fetchLabels = async (id) => { + try { + setPredictionsLoading(true); + + const response = await fetch( + `${API_BASE_URL}/v1/projects/${PROJECT_ID}/${ + model === 'instance_segmentor' + ? 'instance_segmentation' + : 'object_detection' + }?include_border_mask=true`, + { + headers: HEADERS, + method: 'POST', + body: JSON.stringify({ + model_id: modelId, + upload_id: id, + confidence_threshold: confidence / 100, + masker_threshold: threshold / 100, + }), + }, + ); + + const json = await response.json(); + + setLabels( + json.map((inf) => ({ + ...inf, + x: inf.bbox[0], + width: inf.bbox[2] - inf.bbox[0], + y: inf.bbox[1], + height: inf.bbox[3] - inf.bbox[1], + })), + ); + setLastValues({ confidence, threshold }); + } catch (error) { + console.error(error); + } finally { + setPredictionsLoading(false); + } + }; + + const handleChange = (event) => { + setModel(event.target.value); + setLabels([]); + setImageUrl(null); + setImageSizes({ width: 0, height: 0 }); + + if (inputRef.current) { + inputRef.current.value = null; + } }; const handleModelStatusCheck = async () => { - setModelStatus("Checking ..."); - const url = `${API_BASE_URL}/v1/projects/${projectId}/instance_segmentor`; + setModelStatus('Checking...'); + const url = `${API_BASE_URL}/v1/projects/${PROJECT_ID}/${model}`; + try { - const response = await fetch(url, { headers }); + const response = await fetch(url, { headers: HEADERS }); const json = await response.json(); - setModelStatus(json.status || "Error"); + + setModelStatus(json.status || MODEL_STATUS.ERROR); setModelId(json.model_id || null); } catch (e) { - setModelStatus("Error"); + setModelStatus('Error'); + } + }; + + const fetchLabelClasses = async () => { + const url = `${API_BASE_URL}/v1/projects/${PROJECT_ID}/label_classes`; + const labelClassesResponse = await fetch(url, { + headers: HEADERS, + method: 'GET', + }); + const labelClassesJson = await labelClassesResponse.json(); + + setLabelClasses(labelClassesJson.items); + }; + + const refetch = () => { + if ( + !isEqual(lastValues, { confidence, threshold }) && + confidence !== '' && + threshold !== '' + ) { + fetchLabels(uploadId); } }; const handleUpload = async (e) => { - const signedUrlsUrl = `${API_BASE_URL}/v1/projects/${projectId}/image_uploads`; + const signedUrlsUrl = `${API_BASE_URL}/v1/projects/${PROJECT_ID}/image_uploads`; + try { + setImageUploading(true); const signedUrlsResponse = await fetch(signedUrlsUrl, { - headers, - method: "POST", + headers: HEADERS, + method: 'POST', body: JSON.stringify({ count: 1 }), }); const urlsJson = await signedUrlsResponse.json(); + if (urlsJson?.items?.[0]) { const { id, url } = urlsJson.items[0]; - const data = await readAsArrayBuffer(e.target.files[0]); + const data = await readAsArrayBuffer(e[0]); - await fetch(`http://localhost:8080/${url}`, { + await fetch(`${PROXY}${url}`, { body: data, - method: "PUT", + method: 'PUT', headers: { - "content-type": "image/*", + 'content-type': 'image/*', }, }); - const imageUrl = await readAsDataURL(e.target.files[0]); - const imageSizes = await getImageSize(e.target.files[0]); + const imageUrl = await readAsDataURL(e[0]); + const imageSizes = await getImageSize(e[0]); + setImageName(e[0].name); setImageUrl(imageUrl); setImageSizes(imageSizes); - const response = await fetch( - `${API_BASE_URL}/v1/projects/${projectId}/instance_segmentation`, - { - headers, - method: "POST", - body: JSON.stringify({ - // confidence_threshold: 0.5, - // max_detections_per_image: 10, - model_id: modelId, - upload_id: id, - }), - } - ); - - const json = await response.json(); - setLables( - json.map((inf) => ({ - ...inf, - x: inf.bbox[0], - width: inf.bbox[2] - inf.bbox[0], - y: inf.bbox[1], - height: inf.bbox[3] - inf.bbox[1], - })) - ); + setUploadId(id); + fetchLabels(id); + fetchLabelClasses(); } } catch (e) { console.error(e); + } finally { + setImageUploading(false); } }; + useEffect(() => { + if (modelStatus !== MODEL_STATUS.LOADED && !intervalRef.current) { + handleModelStatusCheck(); + intervalRef.current = setInterval(handleModelStatusCheck, 3000); + } else if (modelStatus === MODEL_STATUS.LOADED) { + clearInterval(intervalRef.current); + intervalRef.current = setInterval(handleModelStatusCheck, 30000); + } + }, [modelStatus, handleModelStatusCheck]); + + useEffect( + () => () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + }, + [], + ); + + useEffect(() => { + setModelStatus(MODEL_STATUS.UNKNOWN); + }, [model]); + return ( - - - - - - Hasty Inference Primer - - - - - setKey(e.target.value)} - size={110} + + {!showPredictions ? ( + - setProjectId(e.target.value)} - size={110} + ) : null} + {showPredictions ? ( + - - - Model status: {modelStatus} - - + ) : null} + + + + + + Hasty Inference Primer + + + + + PCB Components + + - - -
- {imageUrl && imageSizes.width && ( - - )} -
+ + + + {/**/} + {/*Model*/} + {/**/} + {/**/} + {!showPredictions ? ( + + + Model status:  + + + {modelStatus} + + {modelStatus === MODEL_STATUS.CHECKING && ( + + )} + + ) : null} + {!showPredictions ? ( + + + + Upload your image + + + + + success icon + File uploaded successfully + + + + + + flywheel + + + ) : null} + {showPredictions ? ( + + + + + + + + Image: {imageName} + + {labels.length ? ( + + {labels.length} predicted{' '} + {labels.length === 1 ? 'label' : 'labels'} + + ) : ( + + No predicted labels + + )} + {labelClasses.length ? ( + + ) : null} + + + + + ) : null} + +
); -} - -export default App; +}; diff --git a/src/Camera.jsx b/src/Camera.jsx new file mode 100644 index 0000000..932b4c1 --- /dev/null +++ b/src/Camera.jsx @@ -0,0 +1,20 @@ +import React, { useEffect } from 'react'; + +export const Camera = () => { + async function getMedia(constraints) { + let stream = null; + + try { + stream = await navigator.mediaDevices.getUserMedia(constraints); + /* use the stream */ + } catch (err) { + /* handle the error */ + } + } + + useEffect(() => { + // getMedia({ video: true }); + }, []); + + return null; +}; diff --git a/src/ImageWithAnnotations.jsx b/src/ImageWithAnnotations.jsx deleted file mode 100644 index 1395efb..0000000 --- a/src/ImageWithAnnotations.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import { getObjectFitSize } from './helpers'; - -export const CardMediaWithAnnotations = React.memo( - ({ - originalImageWidth, - labels, - labelTooltip, - showLabelWhiteBox = true, - ...rest - }) => { - const [offsetX, setOffsetX] = useState(0); - const [offsetY, setOffsetY] = useState(0); - const [labelScale, setLabelScale] = useState(0); - - const handleLoad = useCallback( - (e) => { - const sizes = getObjectFitSize(e.currentTarget); - setLabelScale(sizes.width / originalImageWidth); - setOffsetX(sizes.x); - setOffsetY(sizes.y); - }, - [originalImageWidth, rest.imageUrl], - ); - - - return ( -
- - {labels.map((label, i) => { - const labelContent = ( -
- ); - if (labelTooltip) { - return ( - // eslint-disable-next-line react/no-array-index-key - - {labelTooltip(labelContent, label, i)} - - ); - } - - return labelContent; - })} -
- ); - }, -); diff --git a/src/api/api.js b/src/api/api.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/coloredSquare/ColoredSquare.jsx b/src/components/coloredSquare/ColoredSquare.jsx new file mode 100644 index 0000000..7e7d81e --- /dev/null +++ b/src/components/coloredSquare/ColoredSquare.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { getBorderColor } from './helpers'; + +export const ColoredSquare = ({ color }) => ( +
+); diff --git a/src/components/coloredSquare/helpers.js b/src/components/coloredSquare/helpers.js new file mode 100644 index 0000000..800c0ef --- /dev/null +++ b/src/components/coloredSquare/helpers.js @@ -0,0 +1,27 @@ +import chroma from 'chroma-js'; + +const isEnoughContrast = (color1, color2) => + chroma.contrast(color1, color2) > 2; + +export const getBorderColor = (rectColor, background) => { + const step = 0.2; + const defaultColor = 'white'; + let currentColor = rectColor; + let keepAdjusting; + + do { + let chromaColor; + try { + chromaColor = chroma(currentColor); + } catch (e) { + chromaColor = chroma(defaultColor); + } + const brighterColor = chromaColor.brighten(step).hex(); + keepAdjusting = + !isEnoughContrast(background, brighterColor) && + currentColor !== brighterColor; + currentColor = brighterColor; + } while (keepAdjusting); + + return currentColor; +}; diff --git a/src/components/imageMediaWithAnnotations/ImageWithAnnotations.jsx b/src/components/imageMediaWithAnnotations/ImageWithAnnotations.jsx new file mode 100644 index 0000000..3b26e37 --- /dev/null +++ b/src/components/imageMediaWithAnnotations/ImageWithAnnotations.jsx @@ -0,0 +1,161 @@ +import React, { useCallback, useMemo, useState } from 'react'; +import chroma from 'chroma-js'; +import { getObjectFitSize } from '../../helpers'; +import { + Box, + CircularProgress, + Paper, + Stack, + Tooltip, + Typography, +} from '@mui/material'; +import { ColoredSquare } from '../coloredSquare/ColoredSquare'; + +export const CardMediaWithAnnotations = React.memo( + ({ + originalImageWidth, + labels, + labelClasses, + showLabelWhiteBox = false, + predictionsLoading, + ...rest + }) => { + const [offsetX, setOffsetX] = useState(0); + const [offsetY, setOffsetY] = useState(0); + const [labelScale, setLabelScale] = useState(0); + + const colorsMap = useMemo(() => { + return labelClasses.reduce((acc, val) => { + acc[val.id] = `rgba(${chroma(val.color).alpha(0.55).rgba()})`; + + return acc; + }, {}); + }, [labelClasses]); + const strokeColorsMap = useMemo(() => { + return labelClasses.reduce((acc, val) => { + acc[val.id] = `rgba(${chroma(val.color).alpha(1).rgba()})`; + + return acc; + }, {}); + }, [labelClasses]); + const classNamesMap = useMemo(() => { + return labelClasses.reduce((acc, val) => { + acc[val.id] = val.name; + + return acc; + }, {}); + }, [labelClasses]); + + const handleLoad = useCallback( + (e) => { + const sizes = getObjectFitSize(e.currentTarget); + setLabelScale(sizes.width / originalImageWidth); + setOffsetX(sizes.x); + setOffsetY(sizes.y); + }, + [originalImageWidth, rest.imageUrl], + ); + + return ( + + {predictionsLoading && ( + <> + + + + )} + label + {labels.map((label, i) => { + const labelContent = ( + + + + + + {classNamesMap[label.class_id]} + + + + Confidence: {label.score.toFixed(2)} + + + ) : ( + '' + ) + } + followCursor + > + + + + + ); + + return labelContent; + })} + + ); + }, +); diff --git a/src/components/labelClasses/LabelClasses.jsx b/src/components/labelClasses/LabelClasses.jsx new file mode 100644 index 0000000..d1904c0 --- /dev/null +++ b/src/components/labelClasses/LabelClasses.jsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { OutlinedInput } from '@mui/material'; +import { Box, FormControl, InputLabel, Stack, Typography } from '@mui/material'; +import { ColoredSquare } from '../coloredSquare/ColoredSquare'; + +export const LabelClasses = ({ + labelClasses, + labels, + setConfidence, + setThreshold, + confidence, + threshold, + refetch, +}) => { + const labelsCountMap = labelClasses + .map(({ id }) => id) + .reduce((acc, val) => { + acc[val] = labels.filter(({ class_id }) => class_id === val).length; + return acc; + }, {}); + + return ( + + + + Confidence + { + let value = event.target.value; + + if (value > 100) { + value = 100; + } + if (value < 0 && value !== '') { + value = 0; + } + + setConfidence(value); + }} + onBlur={refetch} + /> + + + Masker threshold + { + let value = event.target.value; + + if (value > 100) { + value = 100; + } + if (value < 0 && value !== '') { + value = 0; + } + + setThreshold(+value); + }} + onBlur={refetch} + /> + + + Object classes + + {labelClasses.map((labelClass) => ( + + + + {labelClass.name} + + + ({labelsCountMap[labelClass.id]} label + {labelsCountMap[labelClass.id] === 1 ? '' : 's'}) + + + ))} + + + ); +}; diff --git a/src/components/uploadBox/UploadBox.jsx b/src/components/uploadBox/UploadBox.jsx new file mode 100644 index 0000000..a699a7e --- /dev/null +++ b/src/components/uploadBox/UploadBox.jsx @@ -0,0 +1,111 @@ +import React from 'react'; +import { useDropzone } from 'react-dropzone'; +import Upload from '../../img/upload.svg'; +import { CardActionArea, CircularProgress, Typography } from '@mui/material'; + +const fileSizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; +const step = 1024; +const formatFileSize = (bytes, decimals = 2) => { + if (!bytes) return '0 Bytes'; + + const power = Math.floor(Math.log(bytes) / Math.log(step)); + + return `${parseFloat((bytes / step ** power).toFixed(decimals))} ${ + fileSizes[power] + }`; +}; +const acceptedFileTypes = [ + 'image/jpeg', + 'image/jpg', + 'image/jpe', + 'image/png', + 'image/svg', + 'image/bmp', + 'image/webp', + 'image/tiff', +]; + +export const UploadBox = ({ + onDrop, + disabled = false, + inProgress = false, + maxSize, + fileTypesText, + children, +}) => { + const dropzoneProps = useDropzone({ + onDrop, + accept: acceptedFileTypes, + maxSize, + disabled, + }); + const { + getRootProps, + getInputProps, + isDragAccept, + isDragReject, + rejectedFiles, + } = dropzoneProps; + const { ref, ...rootProps } = getRootProps(); + const hasRejectedFiles = rejectedFiles?.length > 0; + + return ( + + + {isDragAccept && Release to upload file} + {inProgress && ( + <> + + Uploading + + )} + {!isDragAccept && !inProgress && !isDragReject && ( + + )} + {hasRejectedFiles && ( + <> + File rejected + + Please note: + + + )} + {(isDragReject || hasRejectedFiles) && ( + <> + + Supported file types:{' '} + {fileTypesText || + acceptedFileTypes + .map((fileType) => fileType.split('/')[1]) + .join(', ')} + . + + + Maximum file size is {formatFileSize(maxSize)}. + + Drag-and-drop of folders is not supported. + + )} + {!isDragAccept && !inProgress && !isDragReject && !hasRejectedFiles && ( + + Drag the image you want to upload here or click to browse files + + )} + {children} + + ); +}; diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..a004404 --- /dev/null +++ b/src/constants.js @@ -0,0 +1,20 @@ +export const PROXY = + process.env.NODE_ENV === 'development' ? 'http://localhost:8080/' : ''; +export const API_BASE_URL = `${PROXY}https://api.hasty.ai`; +export const API_KEY = + 'zX0ZBRL7Kml4w2jidFjh917IZX5sdY9YEpm0hY2QZDIg9gLdogniIzNHTfQN2TGET-cO4NK9ry9cRGRQ9-y8WQ'; +// export const API_KEY = +// '19hkg36JsCn5VlaMHU9xaHpUz2GzPMxp3b5FE6NEyyYzQvuTZn0o67CaXKsPQ2IAj5kvAMXy3inMV-jNV4vJ8Q'; +// export const PROJECT_ID = '8061497a-2eb9-40c1-971c-b05d07f5a7e7'; +export const PROJECT_ID = '8061497a-2eb9-40c1-971c-b05d07f5a7e7'; +export const MODEL_STATUS = { + LOADED: 'LOADED', + ERROR: 'ERROR', + UNKNOWN: 'UNKNOWN', + LOADING: 'LOADING', + CHECKING: 'Checking...', +}; +export const HEADERS = { + 'X-Api-Key': API_KEY, + 'content-type': 'application/json', +}; diff --git a/src/icons/hog.svg b/src/icons/hog.svg deleted file mode 100644 index 63a7a92..0000000 --- a/src/icons/hog.svg +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/img/bottomLeftPattern.svg b/src/img/bottomLeftPattern.svg new file mode 100644 index 0000000..9d2fe97 --- /dev/null +++ b/src/img/bottomLeftPattern.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/img/flywheel.svg b/src/img/flywheel.svg new file mode 100644 index 0000000..1cd97c4 --- /dev/null +++ b/src/img/flywheel.svg @@ -0,0 +1,529 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/img/hog.svg b/src/img/hog.svg new file mode 100644 index 0000000..9ea5ee9 --- /dev/null +++ b/src/img/hog.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/img/rightPattern.svg b/src/img/rightPattern.svg new file mode 100644 index 0000000..e3be922 --- /dev/null +++ b/src/img/rightPattern.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/img/success.svg b/src/img/success.svg new file mode 100644 index 0000000..b6cbe50 --- /dev/null +++ b/src/img/success.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/img/upload.svg b/src/img/upload.svg new file mode 100644 index 0000000..f392dc9 --- /dev/null +++ b/src/img/upload.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/index.js b/src/index.js index ef2edf8..85b7baf 100644 --- a/src/index.js +++ b/src/index.js @@ -1,14 +1,14 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; -import App from './App'; +import { App } from './App'; import reportWebVitals from './reportWebVitals'; ReactDOM.render( , - document.getElementById('root') + document.getElementById('root'), ); // If you want to start measuring performance in your app, pass a function diff --git a/src/theme.js b/src/theme.js index ae78751..4537adc 100644 --- a/src/theme.js +++ b/src/theme.js @@ -1,42 +1,42 @@ -import { createTheme } from "@mui/material"; -import shadows from "@mui/material/styles/shadows"; +import { createTheme } from '@mui/material'; +import shadows from '@mui/material/styles/shadows'; const palette = { - mode: "dark", + mode: 'dark', primary: { - main: "#2164aa", - contrastText: "#dfdfdf", - light: "#46BFEF", + main: '#2164aa', + contrastText: '#dfdfdf', + light: '#46BFEF', }, secondary: { - main: "#33373f", - contrastText: "#dfdfdf", + main: '#33373f', + contrastText: '#dfdfdf', }, interactive: { - main: "#1e1f28", + main: '#1e1f28', }, success: { - main: "#00DAA4", + main: '#00DAA4', }, error: { - main: "#f3325e", + main: '#f3325e', }, neutral: { - light: "#999999", - main: "#636363", + light: '#999999', + main: '#636363', }, action: { - hover: "#33373f", + hover: '#33373f', }, text: { - primary: "#dfdfdf", - secondary: "#8e9196", + primary: '#dfdfdf', + secondary: '#8e9196', }, background: { - paper: "#34373E", - default: "#282930", - menu: "#2d2f36", - body: "#282930", + paper: '#34373E', + default: '#282930', + menu: '#2d2f36', + body: '#EEF2F8', }, }; @@ -46,46 +46,46 @@ export const theme = createTheme({ fontFamily: "'Maven Pro', sans-serif", fontSize: 13, button: { - textTransform: "none", + textTransform: 'none', fontWeight: 500, lineHeight: 1, - fontSize: "1rem", + fontSize: '1rem', }, body1: { - fontSize: "1rem", + fontSize: '1rem', }, body2: { - fontSize: "0.92rem", + fontSize: '0.92rem', }, h1: { fontSize: 22, - lineHeight: "26px", + lineHeight: '26px', // @ts-ignore - fontWeight: "bold!important", + fontWeight: 'bold!important', }, h2: { // @ts-ignore - fontWeight: "bold!important", + fontWeight: 'bold!important', }, h3: { // @ts-ignore - fontWeight: "bold!important", + fontWeight: 'bold!important', }, h4: { // @ts-ignore - fontWeight: "bold!important", + fontWeight: 'bold!important', }, h5: { // @ts-ignore - fontWeight: "bold!important", + fontWeight: 'bold!important', }, h6: { fontSize: 16, lineHeight: 1.7, - fontWeight: "bold", + fontWeight: 'bold', }, subtitle1: { - fontSize: "1.3rem", + fontSize: '1.3rem', }, }, shape: { @@ -106,38 +106,38 @@ export const theme = createTheme({ styleOverrides: { root: { borderRadius: 0, - backgroundImage: "none", - overflow: "visible!important", // TODO: Revert to auto when fully switched to MUI + backgroundImage: 'none', + overflow: 'visible!important', // TODO: Revert to auto when fully switched to MUI }, }, }, MuiButton: { defaultProps: { - variant: "contained", + variant: 'contained', disableElevation: true, }, styleOverrides: { root: { borderRadius: 0, - lineHeight: "1.7rem", + lineHeight: '1.7rem', fontWeight: 700, - fontSize: "1rem", - whiteSpace: "nowrap", + fontSize: '1rem', + whiteSpace: 'nowrap', }, sizeSmall: { - fontWeight: "normal", - fontSize: "0.9rem", + fontWeight: 'normal', + fontSize: '0.9rem', }, outlined: { border: `1px dotted #fff`, color: palette.text.primary, background: palette.interactive.main, - "&:hover": { + '&:hover': { background: palette.neutral.main, - border: "1px dotted rgba(255, 255, 255, 0.3)", + border: '1px dotted rgba(255, 255, 255, 0.3)', }, - ":disabled": { - border: "1px dotted rgba(255, 255, 255, 0.3)", + ':disabled': { + border: '1px dotted rgba(255, 255, 255, 0.3)', }, }, }, @@ -162,7 +162,7 @@ export const theme = createTheme({ MuiCardContent: { styleOverrides: { root: { - "&:last-child": { + '&:last-child': { paddingBottom: 16, }, }, @@ -184,34 +184,34 @@ export const theme = createTheme({ }, styleOverrides: { root: { - backgroundColor: "#1e1f28", + backgroundColor: '#1e1f28', borderRadius: 0, // @ts-ignore - "&.Mui-error .MuiOutlinedInput-notchedOutline": { - borderStyle: "solid!important", - borderWidth: "1px!important", + '&.Mui-error .MuiOutlinedInput-notchedOutline': { + borderStyle: 'solid!important', + borderWidth: '1px!important', borderColor: ({ palette }) => // @ts-ignore palette?.error?.light, }, }, notchedOutline: { - border: "none", + border: 'none', }, input: { - padding: "7.5px 14px", + padding: '7.5px 14px', }, }, }, MuiInputLabel: { styleOverrides: { root: { - transform: "none", - position: "static", + transform: 'none', + position: 'static', paddingBottom: 6, - color: "#939394", - display: "flex", - alignItems: "center", + color: '#939394', + display: 'flex', + alignItems: 'center', }, }, }, @@ -219,7 +219,7 @@ export const theme = createTheme({ styleOverrides: { root: { borderRadius: 0, - fontSize: "1rem", + fontSize: '1rem', backgroundColor: palette.interactive.main, }, }, @@ -227,7 +227,7 @@ export const theme = createTheme({ MuiList: { styleOverrides: { root: { - backgroundImage: "none", + backgroundImage: 'none', backgroundColor: palette.background.menu, }, }, @@ -242,11 +242,11 @@ export const theme = createTheme({ MuiTable: { styleOverrides: { root: { - backgroundColor: "#282930", + backgroundColor: '#282930', }, stickyHeader: { - ".MuiTableCell-head": { - backgroundColor: "#282930", + '.MuiTableCell-head': { + backgroundColor: '#282930', }, }, }, @@ -254,20 +254,20 @@ export const theme = createTheme({ MuiTableCell: { styleOverrides: { root: { - border: "none", - fontSize: "1rem", - padding: "0.8rem 1.2rem", + border: 'none', + fontSize: '1rem', + padding: '0.8rem 1.2rem', }, head: { - color: "#888A8E", - borderBottom: `1px solid ${"#33373f"}`, - fontWeight: "normal", - whiteSpace: "nowrap", + color: '#888A8E', + borderBottom: `1px solid ${'#33373f'}`, + fontWeight: 'normal', + whiteSpace: 'nowrap', }, // Our theme was overriding the padding prop passed to TableCell, // so this over-overrides it back to normal behavior. paddingNone: { - padding: "none", + padding: 'none', }, }, }, @@ -277,31 +277,31 @@ export const theme = createTheme({ }, styleOverrides: { root: { - "&:nth-of-type(even)": { - backgroundColor: "#33373f", - "& > .MuiTableCell-root": { - backgroundColor: "#33373f", + '&:nth-of-type(even)': { + backgroundColor: '#33373f', + '& > .MuiTableCell-root': { + backgroundColor: '#33373f', }, }, - "&:nth-of-type(odd)": { - backgroundColor: "#282930", - "& > .MuiTableCell-root": { - backgroundColor: "#282930", + '&:nth-of-type(odd)': { + backgroundColor: '#282930', + '& > .MuiTableCell-root': { + backgroundColor: '#282930', }, }, - "&:hover": { - backgroundColor: `${"#636363"}!important`, - "& > .MuiTableCell-root": { - backgroundColor: `${"#636363"}!important`, + '&:hover': { + backgroundColor: `${'#636363'}!important`, + '& > .MuiTableCell-root': { + backgroundColor: `${'#636363'}!important`, }, }, }, head: { - "&:hover": { - backgroundColor: "inherit", + '&:hover': { + backgroundColor: 'inherit', }, - "&:nth-of-type(even)": { - backgroundColor: "inherit", + '&:nth-of-type(even)': { + backgroundColor: 'inherit', }, }, }, @@ -309,7 +309,9 @@ export const theme = createTheme({ MuiTooltip: { styleOverrides: { tooltip: { - fontSize: "1rem", + fontSize: '1rem', + padding: 0, + borderRadius: '3px', }, }, }, @@ -329,12 +331,12 @@ export const theme = createTheme({ styleOverrides: { root: { backgroundColor: palette.interactive.main, - padding: "0px 8px", + padding: '0px 8px', minHeight: 36, }, content: { margin: 0, - alignItems: "center", + alignItems: 'center', }, }, }, @@ -348,26 +350,26 @@ export const theme = createTheme({ MuiSelect: { styleOverrides: { root: { - display: "flex", - alignItems: "center", + display: 'flex', + alignItems: 'center', }, filled: { - padding: "0.6rem", + padding: '0.6rem', }, icon: { - top: "calc(50% - 9px)", + top: 'calc(50% - 9px)', }, }, }, MuiAutocomplete: { styleOverrides: { inputRoot: { - "&.MuiOutlinedInput-root": { - padding: "0px 8px", + '&.MuiOutlinedInput-root': { + padding: '0px 8px', }, }, endAdornment: { - top: "calc(50% - 12px)", + top: 'calc(50% - 12px)', }, paper: { boxShadow: shadows[3], @@ -384,9 +386,9 @@ export const theme = createTheme({ MuiDialog: { styleOverrides: { container: { - height: "unset", - marginTop: "4rem", - marginBottom: "4rem", + height: 'unset', + marginTop: '4rem', + marginBottom: '4rem', }, paper: { backgroundColor: palette.background.body, @@ -398,13 +400,13 @@ export const theme = createTheme({ // @ts-ignore root: { backgroundColor: palette.interactive.main, - padding: "6px 24px", - "& > .MuiTypography-root": { + padding: '6px 24px', + '& > .MuiTypography-root': { marginBottom: 0, - textTransform: "uppercase", - color: "#8e9196", - fontSize: "1.1rem", - fontWeight: "500!important", + textTransform: 'uppercase', + color: '#8e9196', + fontSize: '1.1rem', + fontWeight: '500!important', }, }, }, @@ -412,30 +414,30 @@ export const theme = createTheme({ MuiDialogContent: { styleOverrides: { root: { - padding: "1.5rem !important", - overflow: "visible", + padding: '1.5rem !important', + overflow: 'visible', }, }, }, MuiDialogActions: { styleOverrides: { root: { - padding: "1rem", + padding: '1rem', }, }, }, MuiModal: { styleOverrides: { root: { - overflow: "auto", + overflow: 'auto', }, }, }, MuiMenuItem: { styleOverrides: { root: { - ":hover": { - color: "inherit", + ':hover': { + color: 'inherit', }, }, }, @@ -459,7 +461,7 @@ export const theme = createTheme({ MuiAlert: { styleOverrides: { message: { - fontSize: "1rem", + fontSize: '1rem', }, }, }, diff --git a/yarn.lock b/yarn.lock index 45b18dc..055e4db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1112,6 +1112,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.17.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" + integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.12.13", "@babel/template@^7.3.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" @@ -1528,6 +1535,13 @@ prop-types "^15.7.2" react-is "^17.0.2" +"@mui/icons-material@^5.6.2": + version "5.6.2" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.6.2.tgz#239c40fc5841dc7c6af7d00e4e988550de170fcd" + integrity sha512-9QdI7axKuBAyaGz4mtdi7Uy1j73/thqFmEuxpJHxNC7O8ADEK1Da3t2veK2tgmsXsUlAHcAG63gg+GvWWeQNqQ== + dependencies: + "@babel/runtime" "^7.17.2" + "@mui/material@^5.0.6": version "5.0.6" resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.0.6.tgz#0e688c918fd88a07f59385614c65fce937077a9f" @@ -2719,6 +2733,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +attr-accept@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + autoprefixer@^9.6.1: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" @@ -3433,6 +3452,11 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chroma-js@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chroma-js/-/chroma-js-2.4.2.tgz#dffc214ed0c11fa8eefca2c36651d8e57cbfb2b0" + integrity sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A== + chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" @@ -3760,6 +3784,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cors-anywhere@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cors-anywhere/-/cors-anywhere-0.4.4.tgz#98892fcab55f408fff13a63e125135c18dc22ca8" + integrity sha512-8OBFwnzMgR4mNrAeAyOLB2EruS2z7u02of2bOu7i9kKYlZG+niS7CTHLPgEXKWW2NAOJWRry9RRCaL9lJRjNqg== + dependencies: + http-proxy "1.11.1" + proxy-from-env "0.0.1" + cosmiconfig@^5.0.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -4933,6 +4965,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +eventemitter3@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" + integrity sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg= + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -5176,6 +5213,13 @@ file-loader@6.1.1: loader-utils "^2.0.0" schema-utils "^3.0.0" +file-selector@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc" + integrity sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw== + dependencies: + tslib "^2.4.0" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -5848,6 +5892,14 @@ http-proxy-middleware@0.19.1: lodash "^4.17.11" micromatch "^3.1.10" +http-proxy@1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.11.1.tgz#71df55757e802d58ea810df2244019dda05ae85d" + integrity sha1-cd9VdX6ALVjqgQ3yJEAZ3aBa6F0= + dependencies: + eventemitter3 "1.x.x" + requires-port "0.x.x" + http-proxy@^1.17.0: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" @@ -7225,7 +7277,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5: +"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8997,6 +9049,11 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +prettier@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== + pretty-bytes@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -9074,6 +9131,15 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -9082,6 +9148,11 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.1" +proxy-from-env@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-0.0.1.tgz#b27c4946e9e6d5dbadb7598a6435d3014c4cfd49" + integrity sha1-snxJRunm1dutt1mKZDXTAUxM/Uk= + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -9280,12 +9351,21 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-dropzone@^14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.0.tgz#97faaaf2e0a76daadfa678232be67d59ce891449" + integrity sha512-D7AXPtRba8rd7DBOejh3W2v1Uax6i7XKPYPuMr13XFPfnDcPHHvlEfp3raVpdj3XMHlRfYuf2H5+m8p7mlgKdQ== + dependencies: + attr-accept "^2.2.2" + file-selector "^0.6.0" + prop-types "^15.8.1" + react-error-overlay@^6.0.9: version "6.0.9" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== -react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -9645,6 +9725,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requires-port@0.x.x: + version "0.0.1" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-0.0.1.tgz#4b4414411d9df7c855995dd899a8c78a2951c16d" + integrity sha1-S0QUQR2d98hVmV3YmajHiilRwW0= + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -10885,6 +10970,11 @@ tslib@^2.0.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tsutils@^3.17.1: version "3.20.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698"