diff --git a/nerfactory/viewer/app/.eslintrc.json b/nerfactory/viewer/app/.eslintrc.json
index e6eb3ed02..8884cfb12 100644
--- a/nerfactory/viewer/app/.eslintrc.json
+++ b/nerfactory/viewer/app/.eslintrc.json
@@ -17,7 +17,11 @@
"ecmaVersion": "latest",
"sourceType": "module"
},
- "plugins": ["react", "@typescript-eslint"],
+ "plugins": [
+ "react",
+ "@typescript-eslint",
+ "unused-imports"
+ ],
"rules": {
"arrow-body-style": "off",
"camelcase": "off",
@@ -25,6 +29,7 @@
"no-alert": "off",
"no-console": "off",
"prefer-destructuring": "off",
- "react/destructuring-assignment": "off"
+ "react/destructuring-assignment": "off",
+ "unused-imports/no-unused-imports-ts": 2
}
-}
+}
\ No newline at end of file
diff --git a/nerfactory/viewer/app/package.json b/nerfactory/viewer/app/package.json
index 365c29d94..4ac52a07f 100644
--- a/nerfactory/viewer/app/package.json
+++ b/nerfactory/viewer/app/package.json
@@ -4,6 +4,9 @@
"version": "0.0.0",
"private": true,
"dependencies": {
+ "@emotion/react": "^11.10.4",
+ "@emotion/styled": "^11.10.4",
+ "@mui/material": "^5.10.3",
"@reduxjs/toolkit": "^1.8.3",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
@@ -12,14 +15,20 @@
"classnames": "^2.3.1",
"dat.gui": "^0.7.9",
"eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-unused-imports": "^2.0.0",
"leva": "^0.9.29",
"msgpack-lite": "^0.1.26",
"prop-types": "^15.8.1",
+ "re-resizable": "^6.9.9",
"react": "^18.1.0",
"react-dom": "^18.1.0",
+ "react-icons": "^4.4.0",
+ "react-pro-sidebar": "^0.7.1",
"react-redux": "^8.0.2",
"redux": "^4.2.0",
+ "sass": "^1.54.8",
"socket.io-client": "^4.5.1",
+ "stats.js": "^0.17.0",
"three": "^0.142.0",
"three-wtm": "^1.0",
"websocket": "^1.0.34",
diff --git a/nerfactory/viewer/app/src/App.css b/nerfactory/viewer/app/src/App.css
deleted file mode 100644
index ec2585e8c..000000000
--- a/nerfactory/viewer/app/src/App.css
+++ /dev/null
@@ -1,13 +0,0 @@
-body {
- 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;
-}
-
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
- monospace;
-}
diff --git a/nerfactory/viewer/app/src/App.jsx b/nerfactory/viewer/app/src/App.jsx
index 887aa4054..7f08e1550 100644
--- a/nerfactory/viewer/app/src/App.jsx
+++ b/nerfactory/viewer/app/src/App.jsx
@@ -1,21 +1,34 @@
-import './App.css';
-
import React from 'react';
-import { Leva } from 'leva';
-import { RenderControls } from './modules/ConfigPanel/ConfigPanel';
-import LevaTheme from './modules/ConfigPanel/leva_theme.json';
-import SetupScene from './modules/Scene/Scene';
+import { BasicTabs } from './modules/SidePanel/SidePanel';
+import {
+ SceneTreeWebSocketListener,
+ get_scene_tree,
+} from './modules/Scene/Scene';
+import { RenderControls } from './modules/ConfigPanel/ConfigPanel';
import ViewerWindow from './modules/ViewerWindow/ViewerWindow';
+import Banner from './modules/Banner';
export default function App() {
- const scene = SetupScene();
+ // The scene tree won't rerender but it will listener to changes
+ // from the redux store and draw three.js objects.
+ const sceneTree = get_scene_tree();
return (
-
-
-
+ {/*
+ Code that listens for websocket 'write' messages and updates the redux store.
+ */}
+
+ {/* ----- */}
+
+
);
}
diff --git a/nerfactory/viewer/app/src/SceneNode.js b/nerfactory/viewer/app/src/SceneNode.js
index edfa23d58..4fd179074 100644
--- a/nerfactory/viewer/app/src/SceneNode.js
+++ b/nerfactory/viewer/app/src/SceneNode.js
@@ -2,17 +2,6 @@
/* eslint-disable no-restricted-syntax */
import * as THREE from 'three';
-const dat = require('dat.gui');
-
-function remove_folders(gui) {
- for (const name of Object.keys(gui.__folders)) {
- const folder = gui.__folders[name];
- remove_folders(folder);
- dat.dom.dom.unbind(window, 'resize', folder.__resizeHandler);
- gui.removeFolder(folder);
- }
-}
-
function dispose(object) {
if (!object) {
return;
@@ -38,19 +27,16 @@ function dispose(object) {
}
export default class SceneNode {
- constructor(object, folder) {
+ constructor(object) {
this.object = object;
- this.folder = folder;
this.children = {};
- this.create_controls();
for (const c of this.object.children) {
this.add_child(c);
}
}
add_child(object) {
- const f = this.folder.addFolder(object.name);
- const node = new SceneNode(object, f);
+ const node = new SceneNode(object);
this.children[object.name] = node;
return node;
}
@@ -74,19 +60,6 @@ export default class SceneNode {
return child.find(path.slice(1));
}
- create_controls() {
- if (this.vis_controller !== undefined) {
- this.folder.domElement.removeChild(this.vis_controller.domElement);
- }
- this.vis_controller = new dat.controllers.BooleanController(
- this.object,
- 'visible',
- );
- this.folder.domElement.prepend(this.vis_controller.domElement);
- this.vis_controller.domElement.style.height = '0';
- this.vis_controller.domElement.style.float = 'right';
- }
-
set_property(property, value) {
if (property === 'position') {
this.object.position.set(value[0], value[1], value[2]);
@@ -116,7 +89,6 @@ export default class SceneNode {
this.object.parent.remove(this.object);
this.object = object;
parent.add(object);
- this.create_controls();
}
dispose_recursive() {
@@ -136,8 +108,6 @@ export default class SceneNode {
if (child !== undefined) {
child.dispose_recursive();
parent.object.remove(child.object);
- remove_folders(child.folder);
- parent.folder.removeFolder(child.folder);
delete parent.children[name];
}
}
diff --git a/nerfactory/viewer/app/src/index.jsx b/nerfactory/viewer/app/src/index.jsx
index 7ab345c5c..855be0964 100644
--- a/nerfactory/viewer/app/src/index.jsx
+++ b/nerfactory/viewer/app/src/index.jsx
@@ -1,3 +1,4 @@
+import './index.scss';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
diff --git a/nerfactory/viewer/app/src/index.scss b/nerfactory/viewer/app/src/index.scss
new file mode 100644
index 000000000..778d7d65d
--- /dev/null
+++ b/nerfactory/viewer/app/src/index.scss
@@ -0,0 +1,160 @@
+body {
+ 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;
+ height: 100%;
+ overflow: hidden;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
+
+.App {
+ position: relative;
+ height: 100vh;
+}
+
+$banner-color: #84c3ff4d;
+
+.banner {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 50px;
+ width: 100%;
+ z-index: 1000;
+ background-color: $banner-color;
+
+ .banner-button {
+ margin: 10px;
+ float: left;
+ }
+}
+
+.App-body {
+ position: absolute;
+ top: 50px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+$SidePanel-width: 500px;
+$custom-border: solid 1px #ddd;
+
+.SidePanel {
+ position: absolute;
+ background-color: #f0f0f080;
+ top: 0;
+ width: $SidePanel-width;
+ right: 0;
+ bottom: 0;
+ border: $custom-border;
+ z-index: 999;
+ text-align: center;
+}
+
+.Leva-container > div {
+ position: relative;
+ margin: auto;
+ width: 100%;
+ padding: 10px;
+}
+
+.Scene-container {
+ height: 500px;
+}
+
+// Your variable overrides
+$sidebar-color: #b71f0b !default;
+$sidebar-width: 100% !default;
+@import '~react-pro-sidebar/dist/scss/styles.scss';
+
+// ------------------------------------
+// Status Panel styling
+.StatusPanel {
+ display: block;
+ // height: 170px;
+}
+
+.StatusPanel-title {
+ margin: 5px;
+ text-align: center;
+ font-size: 1em;
+ font-weight: bold;
+}
+
+.StatusPanel .StatusPanel-button {
+ margin: 20px;
+}
+
+.StatusPanel .WebSocketUrlField {
+ // display: block;
+ // float: left;
+ margin: 10px;
+}
+
+.StatusPanel-metrics {
+ background-color: $banner-color;
+}
+
+.StatusPanel-metrics div {
+ position: relative;
+ font-size: 16px;
+ display: block;
+ text-align: left;
+}
+
+// ------------------------------------
+// Landing Modal styling
+// .LandingModal {
+// display: block;
+// }
+
+$background-paper-color: #fff;
+
+.LandingModal-box {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 400px; // 400
+ background-color: $background-paper-color;
+ border: 2px solid #000;
+ // box-shadow: 24px 24px;
+ padding: 32px;
+}
+
+.LandingModal-box .WebSocketUrlField {
+ width: 100%;
+}
+
+.LandingModal-box .LandingModal-button {
+ margin: 3px;
+}
+
+// ------------------------------------
+// Camera Vie
+// }
+
+.canvas-container-main,
+.WebRTCVideo {
+ position: absolute;
+ top: 0px;
+ right: $SidePanel-width;
+ bottom: 0px;
+ left: 0px;
+ border: $custom-border;
+}
+
+.WebRTCVideo-video {
+ width: 100%;
+ height: 100%;
+ object-fit: fill;
+}
diff --git a/nerfactory/viewer/app/src/modules/Banner/Banner.jsx b/nerfactory/viewer/app/src/modules/Banner/Banner.jsx
new file mode 100644
index 000000000..17062fafb
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/Banner/Banner.jsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import { useDispatch } from 'react-redux';
+
+import Button from '@mui/material/Button';
+import Menu from '@mui/material/Menu';
+import MenuItem from '@mui/material/MenuItem';
+import LandingModal from '../LandingModal';
+
+function getParam(param_name) {
+ // https://stackoverflow.com/questions/831030/how-to-get-get-request-parameters-in-javascript
+ const params = new RegExp(
+ `[?&]${encodeURIComponent(param_name)}=([^&]*)`,
+ ).exec(window.location.href);
+ if (params === null) {
+ return undefined;
+ }
+ return decodeURIComponent(params[1]);
+}
+
+function getWebsocketEndpoint() {
+ const endpoint = getParam('websocket_url');
+ return endpoint;
+}
+
+export default function Banner() {
+ // const [anchorEl, setAnchorEl] = React.useState(null);
+ const [anchorEl, setAnchorEl] = React.useState(0);
+ const dispatch = useDispatch();
+
+ const open = Boolean(anchorEl);
+ const handleClick = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget);
+ };
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
+
+ let open_modal = true;
+ const websocket_url_from_argument = getWebsocketEndpoint();
+ if (websocket_url_from_argument !== undefined) {
+ open_modal = false;
+ dispatch({
+ type: 'write',
+ path: 'websocketState/websocket_url',
+ data: websocket_url_from_argument,
+ });
+ }
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/nerfactory/viewer/app/src/modules/Banner/index.jsx b/nerfactory/viewer/app/src/modules/Banner/index.jsx
new file mode 100644
index 000000000..6c682527f
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/Banner/index.jsx
@@ -0,0 +1,3 @@
+import Banner from './Banner';
+
+export default Banner;
\ No newline at end of file
diff --git a/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.css b/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.css
deleted file mode 100644
index e69de29bb..000000000
diff --git a/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx b/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
index c26e42b38..4fe9bbdb7 100644
--- a/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
+++ b/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
@@ -9,12 +9,6 @@ const msgpack = require('msgpack-lite');
export function RenderControls() {
// connection status indicators
const websocket = useContext(WebSocketContext).socket;
- const isWebsocketConnected = useSelector(
- (state) => state.websocketState.isConnected,
- );
- const isWebrtcConnected = useSelector(
- (state) => state.webrtcState.isConnected,
- );
const isTraining = useSelector((state) => state.renderingState.isTraining);
const outputOptions = useSelector(
(state) => state.renderingState.output_options,
@@ -37,11 +31,6 @@ export function RenderControls() {
const field_of_view = useSelector(
(state) => state.renderingState.field_of_view,
);
- let eval_fps = useSelector((state) => state.renderingState.eval_fps);
- let train_eta = useSelector((state) => state.renderingState.train_eta);
- let vis_train_ratio = useSelector(
- (state) => state.renderingState.vis_train_ratio,
- );
const dispatch = useDispatch();
@@ -265,44 +254,7 @@ export function RenderControls() {
min_resolution,
max_resolution,
field_of_view,
- ],
- );
-
- const [, setState] = useControls(
- 'Rendering Monitor',
- () => ({
- // WebSocket isConnected
- // button does nothing except be an indicator
- 'WebSocket Connected': button(() => {}, {
- disabled: !isWebsocketConnected,
- }),
- // webRTC isConnected
- // button does nothing except be an indicator
- 'WebRTC Connected': button(() => {}, {
- disabled: !isWebrtcConnected,
- }),
- eval_fps: {
- label: 'Eval FPS',
- value: eval_fps,
- disabled: true,
- },
- train_eta: {
- label: 'Train ETA',
- value: train_eta,
- disabled: true,
- },
- vis_train_ratio: {
- label: 'Time Allocation',
- value: vis_train_ratio,
- disabled: true,
- },
- }),
- [
- isWebsocketConnected,
- isWebrtcConnected,
- eval_fps,
- train_eta,
- vis_train_ratio,
+ websocket // need to re-render when websocket changes to use the new websocket
],
);
@@ -324,26 +276,5 @@ export function RenderControls() {
field_of_view,
]);
- useEffect(() => {}, [setState, isWebsocketConnected, isWebrtcConnected]);
-
- useEffect(() => {
- websocket.addEventListener('message', (originalCmd) => {
- // set the remote description when the offer is received
- const cmd = msgpack.decode(new Uint8Array(originalCmd.data));
- if (cmd.path === '/renderingState/eval_fps') {
- eval_fps = cmd.data;
- setState({ eval_fps });
- }
- if (cmd.path === '/renderingState/train_eta') {
- train_eta = cmd.data;
- setState({ train_eta });
- }
- if (cmd.path === '/renderingState/vis_train_ratio') {
- vis_train_ratio = cmd.data;
- setState({ vis_train_ratio });
- }
- });
- }, []);
-
return null;
}
diff --git a/nerfactory/viewer/app/src/modules/LandingModal/LandingModal.jsx b/nerfactory/viewer/app/src/modules/LandingModal/LandingModal.jsx
new file mode 100644
index 000000000..22d1de6ca
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/LandingModal/LandingModal.jsx
@@ -0,0 +1,80 @@
+import * as React from 'react';
+
+import { Box, Button, Modal, Typography } from '@mui/material';
+
+import WebSocketUrlField from '../WebSocketUrlField';
+
+
+interface LandingModalProps {
+ initial_state: object
+}
+
+export default function LandingModel(props: LandingModalProps) {
+ const [open, setOpen] = React.useState(props.initial_state);
+ const handleOpen = () => setOpen(true);
+ const handleClose = () => setOpen(false);
+
+ const text_1 = `At the top right is the Status Panel where you can connect to a WebSocket and see the train/eval status of your NeRF model. The WebSocket URL takes the form :.`;
+ const text_2 =
+ 'If the WebSocket is not connecting, make sure your port is being forwarded properly!';
+
+ return (
+
+
+
+
+
+ Welcome to the Nerfactory Viewer!{' '}
+
+
+
+
+
Getting Started
+
{text_1}
+
+ You can enter your WebSocket URL in the Status Panel or here:
+
+
+
{text_2}
+
Other Resources:
+
+
+
+
+
+
+
+ );
+}
diff --git a/nerfactory/viewer/app/src/modules/LandingModal/index.jsx b/nerfactory/viewer/app/src/modules/LandingModal/index.jsx
new file mode 100644
index 000000000..f44274051
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/LandingModal/index.jsx
@@ -0,0 +1,3 @@
+import LandingModel from './LandingModal';
+
+export default LandingModel;
\ No newline at end of file
diff --git a/nerfactory/viewer/app/src/modules/Scene/Scene.css b/nerfactory/viewer/app/src/modules/Scene/Scene.css
deleted file mode 100644
index 43ff9bf1a..000000000
--- a/nerfactory/viewer/app/src/modules/Scene/Scene.css
+++ /dev/null
@@ -1,5 +0,0 @@
-#datgui {
- position: absolute;
- top: 2px;
- left: 2px;
-}
diff --git a/nerfactory/viewer/app/src/modules/Scene/Scene.jsx b/nerfactory/viewer/app/src/modules/Scene/Scene.jsx
index 4e5a1b891..f2aff9538 100644
--- a/nerfactory/viewer/app/src/modules/Scene/Scene.jsx
+++ b/nerfactory/viewer/app/src/modules/Scene/Scene.jsx
@@ -1,38 +1,24 @@
-import './Scene.css';
-
/* eslint-disable no-restricted-syntax */
import * as THREE from 'three';
import { useContext, useEffect } from 'react';
import { useDispatch } from 'react-redux';
-
-import { GUI } from 'dat.gui';
-import SceneNode from '../../SceneNode';
-import { subscribe_to_changes } from '../../subscriber';
-import { WebSocketContext } from '../WebSocket/WebSocket';
import { drawCamera, drawSceneBounds } from './drawing';
+import SceneNode from '../../SceneNode';
+import { WebSocketContext } from '../WebSocket/WebSocket';
+import { subscribe_to_changes } from '../../subscriber';
+
const msgpack = require('msgpack-lite');
-// manages setting up the scene and other logic for keeping state in sync with the server
-export default function SetupScene() {
+const SCENE_BOUNDS_NAME = 'Scene Bounds';
+const CAMERAS_NAME = 'Training Cameras';
+
+export function get_scene_tree() {
let scene = null;
- let gui = null;
let sceneTree = null;
-
- // the websocket context
- const socket = useContext(WebSocketContext).socket;
- const dispatch = useDispatch();
-
- // Scene
scene = new THREE.Scene();
-
- // GUI
- gui = new GUI();
- gui.domElement.id = 'datgui';
- const sceneFolder = gui.addFolder('Scene');
- sceneFolder.open();
- sceneTree = new SceneNode(scene, sceneFolder);
+ sceneTree = new SceneNode(scene);
// add objects to the the scene tree
const setObject = (path, object) => {
@@ -64,9 +50,9 @@ export default function SetupScene() {
const fn_value_scene_bounds = (previous, current) => {
if (current !== null) {
const line = drawSceneBounds(current);
- setObject(['Scene Bounds'], line);
+ setObject([SCENE_BOUNDS_NAME], line);
} else {
- deleteObject(['Scene Bounds']);
+ deleteObject([SCENE_BOUNDS_NAME]);
}
};
subscribe_to_changes(selector_fn_scene_bounds, fn_value_scene_bounds);
@@ -92,22 +78,30 @@ export default function SetupScene() {
// keys_valid.push(key);
const json = current[key];
const camera = drawCamera(json);
- setObject(['Cameras', key], camera);
+ setObject([CAMERAS_NAME, key], camera);
}
}
for (const key of prev) {
// invalid so delete
if (!curr.has(key) || current[key] === null) {
// keys_invalid.push(key);
- deleteObject(['Cameras', key]);
+ deleteObject([CAMERAS_NAME, key]);
}
}
} else {
- deleteObject(['Cameras']);
+ deleteObject([CAMERAS_NAME]);
}
};
subscribe_to_changes(selector_fn_cameras, fn_value_cameras);
+ return sceneTree;
+}
+
+// manages setting up the scene and other logic for keeping state in sync with the server
+export function SceneTreeWebSocketListener() {
+ const socket = useContext(WebSocketContext).socket;
+ const dispatch = useDispatch();
+
useEffect(() => {
socket.addEventListener('message', (originalCmd) => {
// set the remote description when the offer is received
@@ -121,7 +115,5 @@ export default function SetupScene() {
});
}
});
- }, []); // empty dependency array means only run once
-
- return scene;
+ }, [socket]); // dependency to call this whenever the websocket changes
}
diff --git a/nerfactory/viewer/app/src/modules/Scene/drawing.js b/nerfactory/viewer/app/src/modules/Scene/drawing.js
index bf411657d..2e8419cdb 100644
--- a/nerfactory/viewer/app/src/modules/Scene/drawing.js
+++ b/nerfactory/viewer/app/src/modules/Scene/drawing.js
@@ -122,7 +122,6 @@ function transpose(matrix) {
export function drawCamera(camera): THREE.Object3D {
const group = new THREE.Group();
- // console.log(camera);
console.assert(
camera.type === 'PinholeCamera',
'The camera should be a PinholeCamera',
diff --git a/nerfactory/viewer/app/src/modules/SidePanel/CameraPanel/CameraPanel.jsx b/nerfactory/viewer/app/src/modules/SidePanel/CameraPanel/CameraPanel.jsx
new file mode 100644
index 000000000..b20f97495
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/SidePanel/CameraPanel/CameraPanel.jsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+
+import { Button } from '@mui/material';
+
+export default function CameraPanel() {
+ return (
+
+ TODO: create a camera path panel list of cameras
+
+
+ );
+}
diff --git a/nerfactory/viewer/app/src/modules/SidePanel/CameraPanel/index.jsx b/nerfactory/viewer/app/src/modules/SidePanel/CameraPanel/index.jsx
new file mode 100644
index 000000000..200115208
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/SidePanel/CameraPanel/index.jsx
@@ -0,0 +1,3 @@
+import CameraPanel from './CameraPanel';
+
+export default CameraPanel;
\ No newline at end of file
diff --git a/nerfactory/viewer/app/src/modules/SidePanel/SidePanel.jsx b/nerfactory/viewer/app/src/modules/SidePanel/SidePanel.jsx
new file mode 100644
index 000000000..028b1d158
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/SidePanel/SidePanel.jsx
@@ -0,0 +1,163 @@
+/* eslint-disable react/jsx-props-no-spreading */
+
+import * as React from 'react';
+
+import { FaLightbulb, FaTractor } from 'react-icons/fa';
+import { Menu, MenuItem, ProSidebar, SubMenu } from 'react-pro-sidebar';
+
+import Box from '@mui/material/Box';
+import { Leva } from 'leva';
+import Tab from '@mui/material/Tab';
+import Tabs from '@mui/material/Tabs';
+import Typography from '@mui/material/Typography';
+import { useEffect } from 'react';
+import StatusPanel from './StatusPanel';
+import SceneNode from '../../SceneNode';
+import LevaTheme from '../ConfigPanel/leva_theme.json';
+import CameraPanel from './CameraPanel';
+
+interface TabPanelProps {
+ children: React.ReactNode;
+ index: number;
+ value: number;
+}
+
+function TabPanel(props: TabPanelProps) {
+ const { children, value, index, ...other } = props;
+
+ return (
+
+ {value === index && (
+
+ {children}
+
+ )}
+
+ );
+}
+
+function a11yProps(index: number) {
+ return {
+ id: `simple-tab-${index}`,
+ 'aria-controls': `simple-tabpanel-${index}`,
+ };
+}
+
+interface ListItemProps {
+ name: object;
+ object: object;
+}
+
+function ListItem(props: ListItemProps) {
+ const name = props.name;
+ const object = props.object;
+
+ const [value, setValue] = React.useState(1);
+ object.visible = value;
+
+ function handleClick() {
+ setValue(!value);
+ }
+
+ return ;
+}
+
+interface ClickableListProps {
+ sceneTree: object
+}
+
+function ClickableList(props: ClickableListProps) {
+ const sceneTree = props.sceneTree;
+
+ const get_menu_items = (name: String, scene_node: SceneNode) => {
+ // TODO: sort the keys by string
+ const num_children = Object.keys(scene_node.children).length;
+ if (num_children === 0) {
+ return (
+ }>
+
+
+ );
+ }
+ return (
+ } defaultOpen>
+ {Object.keys(scene_node.children).map((key) =>
+ get_menu_items(key, scene_node.children[key]),
+ )}
+
+ );
+ };
+
+ const menu_items = get_menu_items('Scene', sceneTree);
+
+ return (
+
+
+
+ );
+}
+
+interface BasicTabsProps {
+ sceneTree: object
+}
+
+export function BasicTabs(props: BasicTabsProps) {
+ const sceneTree = props.sceneTree;
+
+ const [value, setValue] = React.useState(0);
+
+ const handleChange = (event: React.SyntheticEvent, newValue: number) => {
+ setValue(newValue);
+ };
+
+ // set the panel pane index
+ useEffect(() => {
+ const panel_index = 0;
+ setValue(panel_index);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TODO: add a logging panel
+
+
+
+ );
+}
diff --git a/nerfactory/viewer/app/src/modules/SidePanel/StatusPanel/StatusPanel.jsx b/nerfactory/viewer/app/src/modules/SidePanel/StatusPanel/StatusPanel.jsx
new file mode 100644
index 000000000..3c59dded1
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/SidePanel/StatusPanel/StatusPanel.jsx
@@ -0,0 +1,77 @@
+import * as React from 'react';
+
+import Button from '@mui/material/Button';
+import { useSelector } from 'react-redux';
+import WebSocketUrlField from '../../WebSocketUrlField';
+
+
+interface StatusPanelProps {
+ sceneTree: object
+}
+
+export default function StatusPanel(props: StatusPanelProps) {
+
+ const sceneTree = props.sceneTree;
+
+ const isWebsocketConnected = useSelector(
+ (state) => state.websocketState.isConnected,
+ );
+ const isWebrtcConnected = useSelector(
+ (state) => state.webrtcState.isConnected,
+ );
+ const eval_fps = useSelector((state) => state.renderingState.eval_fps);
+ const train_eta = useSelector((state) => state.renderingState.train_eta);
+ const vis_train_ratio = useSelector(
+ (state) => state.renderingState.vis_train_ratio,
+ );
+
+ // simple toggle button
+ const [value, setValue] = React.useState(1);
+ const handleChange = () => {
+ setValue(!value);
+ };
+ const scene_button = value ? "Hide Scene" : "Show Scene";
+ sceneTree.object.visible = value;
+
+ return (
+
+
Status Panel
+
+
+
+
+
+
+ Eval FPS: {eval_fps}
+
+
+ Train ETA: {train_eta}
+
+
+ Time Allocation: {vis_train_ratio}
+
+
+
+ );
+}
diff --git a/nerfactory/viewer/app/src/modules/SidePanel/StatusPanel/index.jsx b/nerfactory/viewer/app/src/modules/SidePanel/StatusPanel/index.jsx
new file mode 100644
index 000000000..2c3fd81ac
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/SidePanel/StatusPanel/index.jsx
@@ -0,0 +1,3 @@
+import StatusPanel from './StatusPanel';
+
+export default StatusPanel;
diff --git a/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.css b/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.css
deleted file mode 100644
index 2390151aa..000000000
--- a/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.css
+++ /dev/null
@@ -1,27 +0,0 @@
-.WebRTCVideo {
- position: absolute;
- top: 0px;
- right: 0px;
- bottom: 0px;
- left: 0px;
-}
-
-.WebRTCVideo-video {
- width: 100%;
- height: 100%;
- object-fit: fill;
-}
-
-.canvas-container-main {
- position: absolute;
- top: 0px;
- right: 0px;
- bottom: 0px;
- left: 0px;
-}
-
-.canvas-container-main canvas {
- position: relative;
- height: 100%;
- width: 100%;
-}
diff --git a/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx b/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
index f5611b586..b82683977 100644
--- a/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
+++ b/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
@@ -1,62 +1,57 @@
-import './ViewerWindow.css';
-
import * as THREE from 'three';
-import React, { useContext, useEffect, useRef } from 'react';
-import { useSelector } from 'react-redux';
-
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
-import WebRtcWindow from '../WebRtcWindow/WebRtcWindow';
+import Stats from 'stats.js';
+import { useSelector } from 'react-redux';
+import React, { useContext, useEffect, useRef } from 'react';
import { WebSocketContext } from '../WebSocket/WebSocket';
+import WebRtcWindow from '../WebRtcWindow/WebRtcWindow';
+
const msgpack = require('msgpack-lite');
+function createStats() {
+ const stats = new Stats();
+ stats.setMode(0);
+ stats.domElement.style.position = 'absolute';
+ stats.domElement.style.left = '0';
+ stats.domElement.style.top = '0';
+ return stats;
+}
+
// manages a camera and the web rtc stream...
export default function ViewerWindow(props) {
// eslint-disable-next-line react/prop-types
const scene = props.scene;
- let cameraControls = null;
+
const myRef = useRef(null);
const websocket = useContext(WebSocketContext).socket;
const field_of_view = useSelector(
(state) => state.renderingState.field_of_view,
);
+ const field_of_view_ref = useRef(field_of_view);
+
+ const camera = useRef(null);
+ let cameraControls = null;
+ let renderer = null;
+ let viewportWidth = null;
+ let viewportHeight = null;
+ let stats = null;
const getViewportWidth = () => {
- return window.innerWidth - (window.innerWidth % 2);
+ const width = myRef.current.clientWidth;
+ return width - (width % 2);
};
const getViewportHeight = () => {
- return window.innerHeight;
+ return myRef.current.clientHeight;
};
- let viewportWidth = getViewportWidth();
- let viewportHeight = getViewportHeight();
-
- const camera = new THREE.PerspectiveCamera(
- field_of_view,
- viewportWidth / viewportHeight,
- 0.01,
- 100,
- );
- camera.position.x = 5;
- camera.position.y = -5;
- camera.position.z = 5;
- camera.up = new THREE.Vector3(0, 0, 1);
-
- const renderer = new THREE.WebGLRenderer({
- antialias: true,
- alpha: true,
- });
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(viewportWidth, viewportHeight);
- renderer.domElement.style.border = '1px solid black';
-
const handleResize = () => {
viewportWidth = getViewportWidth();
viewportHeight = getViewportHeight();
- camera.aspect = viewportWidth / viewportHeight;
- camera.updateProjectionMatrix();
+ camera.current.aspect = viewportWidth / viewportHeight;
+ camera.current.updateProjectionMatrix();
renderer.setSize(viewportWidth, viewportHeight);
};
@@ -68,27 +63,62 @@ export default function ViewerWindow(props) {
const data = {
type: cmd,
path,
- data: camera.toJSON(),
+ data: camera.current.toJSON(),
};
const message = msgpack.encode(data);
websocket.send(message);
}
};
+ // keep sending the camera often
+ useEffect(() => {
+ const fps = 60;
+ const interval = 1000 / fps;
+ const refreshIntervalId = setInterval(sendCamera, interval);
+ return () => {
+ clearInterval(refreshIntervalId);
+ };
+ }, [websocket]);
+
const update = () => {
- handleResize();
- camera.updateProjectionMatrix();
- cameraControls.update();
requestAnimationFrame(update);
- renderer.render(scene, camera);
- sendCamera();
+ handleResize();
+ camera.current.fov = field_of_view_ref.current;
+ camera.current.updateProjectionMatrix();
+ cameraControls.update();
+ renderer.render(scene, camera.current);
+ stats.update();
};
- // similar to componentDidMount
+ // this is run once
useEffect(() => {
+ viewportWidth = getViewportWidth();
+ viewportHeight = getViewportHeight();
+
+ stats = createStats();
+ myRef.current.append(stats.domElement);
+
+ camera.current = new THREE.PerspectiveCamera(
+ field_of_view_ref.current,
+ viewportWidth / viewportHeight,
+ 0.01,
+ 100,
+ );
+ camera.current.position.x = 5;
+ camera.current.position.y = -5;
+ camera.current.position.z = 5;
+ camera.current.up = new THREE.Vector3(0, 0, 1);
+
+ renderer = new THREE.WebGLRenderer({
+ antialias: true,
+ alpha: true,
+ });
+ renderer.setPixelRatio(window.devicePixelRatio);
+ renderer.setSize(viewportWidth, viewportHeight);
+
myRef.current.append(renderer.domElement);
// add camera controls
- cameraControls = new OrbitControls(camera, renderer.domElement);
+ cameraControls = new OrbitControls(camera.current, renderer.domElement);
cameraControls.rotateSpeed = 2.0;
cameraControls.zoomSpeed = 0.3;
cameraControls.panSpeed = 0.2;
@@ -98,19 +128,19 @@ export default function ViewerWindow(props) {
cameraControls.dampingFactor = 1.0;
cameraControls.update();
update();
+
}, []);
- // useEffect(() => {
- // camera.fov = field_of_view;
- // console.log(field_of_view);
- // camera.updateProjectionMatrix();
- // }, [field_of_view]);
+ // updates the field of view inside the ref to avoid rerendering so often
+ useEffect(() => {
+ field_of_view_ref.current = field_of_view;
+ }, [field_of_view]);
return (
-
+ <>
{/* the webrtc viewer needs to know the camera pose */}
-
+ >
);
}
diff --git a/nerfactory/viewer/app/src/modules/WebRtcWindow/WebRtcWindow.jsx b/nerfactory/viewer/app/src/modules/WebRtcWindow/WebRtcWindow.jsx
index 3b93464c6..55398e4cd 100644
--- a/nerfactory/viewer/app/src/modules/WebRtcWindow/WebRtcWindow.jsx
+++ b/nerfactory/viewer/app/src/modules/WebRtcWindow/WebRtcWindow.jsx
@@ -36,7 +36,7 @@ export default function WebRtcWindow() {
username: 'openrelayproject',
credential: 'openrelayproject',
},
- // extra STUN server(s):
+ // Extra STUN and TURN server(s):
// {
// urls: 'stun:stun.l.google.com:19302',
// },
@@ -83,6 +83,15 @@ export default function WebRtcWindow() {
});
}
};
+
+ pc.onclose = () => {
+ dispatch({
+ type: 'write',
+ path: 'webrtcState/isConnected',
+ data: false,
+ });
+ };
+
return pc;
};
@@ -161,7 +170,19 @@ export default function WebRtcWindow() {
console.log('[webrtc] starting process');
sendOffer();
});
- }, []); // empty dependency array means only run once
+
+ // kill the webrtc connection on dismount
+ return () => {
+ if (pcRef.current !== null) {
+ dispatch({
+ type: 'write',
+ path: 'webrtcState/isConnected',
+ data: false,
+ });
+ pcRef.current.close();
+ }
+ };
+ }, [websocket]); // dependency to call this whenever the websocket changes
return (
diff --git a/nerfactory/viewer/app/src/modules/WebSocket/WebSocket.jsx b/nerfactory/viewer/app/src/modules/WebSocket/WebSocket.jsx
index 89c1f7416..934b8b803 100644
--- a/nerfactory/viewer/app/src/modules/WebSocket/WebSocket.jsx
+++ b/nerfactory/viewer/app/src/modules/WebSocket/WebSocket.jsx
@@ -1,49 +1,29 @@
// Much of this code comes from or is inspired by:
// https://www.pluralsight.com/guides/using-web-sockets-in-your-reactredux-app
-import React, { createContext } from 'react';
+import React, { createContext, useEffect } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
-import { useDispatch } from 'react-redux';
const WebSocketContext = createContext(null);
export { WebSocketContext };
-function getParam(param_name) {
- // https://stackoverflow.com/questions/831030/how-to-get-get-request-parameters-in-javascript
- const params = new RegExp(
- `[?&]${encodeURIComponent(param_name)}=([^&]*)`,
- ).exec(window.location.href);
- if (params === null) {
- return undefined;
- }
- return decodeURIComponent(params[1]);
-}
-
-function getWebsocketEndpoint() {
- const endpoint = getParam('websocket_url');
- console.log('websocket url: ', endpoint);
- if (endpoint === undefined) {
- const message =
- 'Please set the websocket endpoint. The format should look like "
?websocket_url=localhost:". E.g., "https://viewer.nerfactory.com/branch/master/?websocket_url=localhost:7007".';
- window.alert(message);
- return '';
- }
- return endpoint;
-}
-
export default function WebSocketContextFunction({ children }) {
const dispatch = useDispatch();
+ let ws = null;
+ let socket = null;
- let socket;
- let ws;
-
- // should look like e.g., "ws://"
- const websocketEndpoint = getWebsocketEndpoint();
+ // this code will rerender anytime the webosocket changes now
+ const websocket_url = useSelector(
+ (state) => state.websocketState.websocket_url,
+ );
+ console.log(websocket_url);
const connect = () => {
- socket = new WebSocket(`ws://${websocketEndpoint}/`);
+ const url = `ws://${websocket_url}/`;
+ socket = new WebSocket(url);
socket.binaryType = 'arraybuffer';
socket.onopen = () => {
dispatch({
@@ -60,9 +40,6 @@ export default function WebSocketContextFunction({ children }) {
path: 'websocketState/isConnected',
data: false,
});
- setTimeout(() => {
- connect();
- }, 1000);
};
socket.onerror = (err) => {
@@ -73,14 +50,20 @@ export default function WebSocketContextFunction({ children }) {
);
socket.close();
};
+ return socket;
};
- if (!socket) {
- connect();
- ws = {
- socket,
+ useEffect(() => {
+ // cleanup function to close the websocket on rerender
+ return () => {
+ socket.close();
};
- }
+ }, [websocket_url]);
+
+ connect();
+ ws = {
+ socket
+ };
return (
{children}
diff --git a/nerfactory/viewer/app/src/modules/WebSocketUrlField.jsx b/nerfactory/viewer/app/src/modules/WebSocketUrlField.jsx
new file mode 100644
index 000000000..6310f9ba4
--- /dev/null
+++ b/nerfactory/viewer/app/src/modules/WebSocketUrlField.jsx
@@ -0,0 +1,30 @@
+import * as React from 'react';
+
+import { TextField } from '@mui/material';
+import { useDispatch, useSelector } from 'react-redux';
+
+export default function WebSocketUrlField() {
+
+ const websocket_url = useSelector(
+ (state) => state.websocketState.websocket_url,
+ );
+ const dispatch = useDispatch();
+ const websocket_url_onchange = (event) => {
+ const value = event.target.value;
+ dispatch({
+ type: 'write',
+ path: 'websocketState/websocket_url',
+ data: value,
+ });
+ };
+
+ return (
+
+ );
+}
diff --git a/nerfactory/viewer/app/src/reducer.js b/nerfactory/viewer/app/src/reducer.js
index 6b6d3b56b..bdfc509a1 100644
--- a/nerfactory/viewer/app/src/reducer.js
+++ b/nerfactory/viewer/app/src/reducer.js
@@ -3,6 +3,7 @@ import { split_path } from './utils';
const initialState = {
// the websocket connection state
websocketState: {
+ websocket_url: 'localhost:7007',
isConnected: false,
},
// the webrtc connection state
diff --git a/nerfactory/viewer/app/yarn.lock b/nerfactory/viewer/app/yarn.lock
index c8a7ea1ec..238148356 100644
--- a/nerfactory/viewer/app/yarn.lock
+++ b/nerfactory/viewer/app/yarn.lock
@@ -162,7 +162,7 @@
dependencies:
"@babel/types" "^7.18.9"
-"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.18.6":
+"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
@@ -513,7 +513,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-jsx@^7.18.6":
+"@babel/plugin-syntax-jsx@^7.17.12", "@babel/plugin-syntax-jsx@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0"
integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==
@@ -1025,7 +1025,7 @@
core-js-pure "^3.20.2"
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.18.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
@@ -1167,6 +1167,114 @@
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36"
integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==
+"@emotion/babel-plugin@^11.10.0":
+ version "11.10.2"
+ resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz#879db80ba622b3f6076917a1e6f648b1c7d008c7"
+ integrity sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==
+ dependencies:
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/plugin-syntax-jsx" "^7.17.12"
+ "@babel/runtime" "^7.18.3"
+ "@emotion/hash" "^0.9.0"
+ "@emotion/memoize" "^0.8.0"
+ "@emotion/serialize" "^1.1.0"
+ babel-plugin-macros "^3.1.0"
+ convert-source-map "^1.5.0"
+ escape-string-regexp "^4.0.0"
+ find-root "^1.1.0"
+ source-map "^0.5.7"
+ stylis "4.0.13"
+
+"@emotion/cache@^11.10.0", "@emotion/cache@^11.10.3":
+ version "11.10.3"
+ resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.3.tgz#c4f67904fad10c945fea5165c3a5a0583c164b87"
+ integrity sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==
+ dependencies:
+ "@emotion/memoize" "^0.8.0"
+ "@emotion/sheet" "^1.2.0"
+ "@emotion/utils" "^1.2.0"
+ "@emotion/weak-memoize" "^0.3.0"
+ stylis "4.0.13"
+
+"@emotion/hash@^0.9.0":
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7"
+ integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==
+
+"@emotion/is-prop-valid@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83"
+ integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==
+ dependencies:
+ "@emotion/memoize" "^0.8.0"
+
+"@emotion/memoize@^0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
+ integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
+
+"@emotion/react@^11.10.4":
+ version "11.10.4"
+ resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.4.tgz#9dc6bccbda5d70ff68fdb204746c0e8b13a79199"
+ integrity sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==
+ dependencies:
+ "@babel/runtime" "^7.18.3"
+ "@emotion/babel-plugin" "^11.10.0"
+ "@emotion/cache" "^11.10.0"
+ "@emotion/serialize" "^1.1.0"
+ "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
+ "@emotion/utils" "^1.2.0"
+ "@emotion/weak-memoize" "^0.3.0"
+ hoist-non-react-statics "^3.3.1"
+
+"@emotion/serialize@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.0.tgz#b1f97b1011b09346a40e9796c37a3397b4ea8ea8"
+ integrity sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==
+ dependencies:
+ "@emotion/hash" "^0.9.0"
+ "@emotion/memoize" "^0.8.0"
+ "@emotion/unitless" "^0.8.0"
+ "@emotion/utils" "^1.2.0"
+ csstype "^3.0.2"
+
+"@emotion/sheet@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.0.tgz#771b1987855839e214fc1741bde43089397f7be5"
+ integrity sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w==
+
+"@emotion/styled@^11.10.4":
+ version "11.10.4"
+ resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.4.tgz#e93f84a4d54003c2acbde178c3f97b421fce1cd4"
+ integrity sha512-pRl4R8Ez3UXvOPfc2bzIoV8u9P97UedgHS4FPX594ntwEuAMA114wlaHvOK24HB48uqfXiGlYIZYCxVJ1R1ttQ==
+ dependencies:
+ "@babel/runtime" "^7.18.3"
+ "@emotion/babel-plugin" "^11.10.0"
+ "@emotion/is-prop-valid" "^1.2.0"
+ "@emotion/serialize" "^1.1.0"
+ "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
+ "@emotion/utils" "^1.2.0"
+
+"@emotion/unitless@^0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db"
+ integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==
+
+"@emotion/use-insertion-effect-with-fallbacks@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df"
+ integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==
+
+"@emotion/utils@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
+ integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
+
+"@emotion/weak-memoize@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
+ integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
+
"@eslint/eslintrc@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
@@ -1492,6 +1600,92 @@
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
+"@mui/base@5.0.0-alpha.95":
+ version "5.0.0-alpha.95"
+ resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.95.tgz#eac88ddb6ded633a73cd3c19638241f0f9fa274f"
+ integrity sha512-fcxnDeO7rBwzP0buVdI5fn0aA7NQ/AeUV5RzIIH0kOXVVT21HB4JFf41Qhwd0PIq63PXxmc6Fs2mdlzMYuPo9g==
+ dependencies:
+ "@babel/runtime" "^7.18.9"
+ "@emotion/is-prop-valid" "^1.2.0"
+ "@mui/types" "^7.2.0"
+ "@mui/utils" "^5.10.3"
+ "@popperjs/core" "^2.11.6"
+ clsx "^1.2.1"
+ prop-types "^15.8.1"
+ react-is "^18.2.0"
+
+"@mui/core-downloads-tracker@^5.10.3":
+ version "5.10.3"
+ resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.3.tgz#e17a3cd87c7814ff35592284b19ae39ad52b85ac"
+ integrity sha512-mX2S0d1oboKBbWQqWIgRmyALAEzh37yiknpD3mKx8bcoMKbp1VtqzIt0aeHP16Uhsd0eValDFILxLNHWi0oddQ==
+
+"@mui/material@^5.10.3":
+ version "5.10.3"
+ resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.10.3.tgz#5497128ea7c8de1d40ba9bd95437d46d441fa1d1"
+ integrity sha512-g0lzHcqWHYeOEAxTzcwpM1I7b+wyiRTeXkEdRsspnOpZtb0H/1xg386tMFRGbxBJ4zfVGT+TWublofw7pyQkqw==
+ dependencies:
+ "@babel/runtime" "^7.18.9"
+ "@mui/base" "5.0.0-alpha.95"
+ "@mui/core-downloads-tracker" "^5.10.3"
+ "@mui/system" "^5.10.3"
+ "@mui/types" "^7.2.0"
+ "@mui/utils" "^5.10.3"
+ "@types/react-transition-group" "^4.4.5"
+ clsx "^1.2.1"
+ csstype "^3.1.0"
+ prop-types "^15.8.1"
+ react-is "^18.2.0"
+ react-transition-group "^4.4.5"
+
+"@mui/private-theming@^5.10.3":
+ version "5.10.3"
+ resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.10.3.tgz#7325eef3e480caaaa2d866b9057943ec4fbcb8ce"
+ integrity sha512-LCYIKlkGz2BTSng2BFzzwSJBRZbChIUri2x2Nh8ryk2B1Ho7zpvE7ex6y39LlStG2Frf92NFC/V4YQbmMAjD5A==
+ dependencies:
+ "@babel/runtime" "^7.18.9"
+ "@mui/utils" "^5.10.3"
+ prop-types "^15.8.1"
+
+"@mui/styled-engine@^5.10.3":
+ version "5.10.3"
+ resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.10.3.tgz#c3e061548951568936b749a58531fb269af48948"
+ integrity sha512-9Uz7eB8xXoiDvpJ9qBxZ/2xGO8xKfA2T23dw4AsQ69SQtGatrOLAapzP2lNr0tfB9xvKucclPFhRO5aLhDFOVQ==
+ dependencies:
+ "@babel/runtime" "^7.18.9"
+ "@emotion/cache" "^11.10.3"
+ csstype "^3.1.0"
+ prop-types "^15.8.1"
+
+"@mui/system@^5.10.3":
+ version "5.10.3"
+ resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.10.3.tgz#26f82e506af506f4fddde265c20e1100baaca03e"
+ integrity sha512-uLW/CIz3zk1jr5zH0ahOUqJIrpWP02Mv4emfrplh7Mh5JCb/oumhYaC/ALJJEjzUHKg9wwiyuM0pCwK/kSf1jQ==
+ dependencies:
+ "@babel/runtime" "^7.18.9"
+ "@mui/private-theming" "^5.10.3"
+ "@mui/styled-engine" "^5.10.3"
+ "@mui/types" "^7.2.0"
+ "@mui/utils" "^5.10.3"
+ clsx "^1.2.1"
+ csstype "^3.1.0"
+ prop-types "^15.8.1"
+
+"@mui/types@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.0.tgz#91380c2d42420f51f404120f7a9270eadd6f5c23"
+ integrity sha512-lGXtFKe5lp3UxTBGqKI1l7G8sE2xBik8qCfrLHD5olwP/YU0/ReWoWT7Lp1//ri32dK39oPMrJN8TgbkCSbsNA==
+
+"@mui/utils@^5.10.3":
+ version "5.10.3"
+ resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.10.3.tgz#ce2a96f31de2a5e717f507b5383dbabbddbc4dfc"
+ integrity sha512-4jXMDPfx6bpMVuheLaOpKTjpzw39ogAZLeaLj5+RJec3E37/hAZMYjURfblLfTWMMoGoqkY03mNsZaEwNobBow==
+ dependencies:
+ "@babel/runtime" "^7.18.9"
+ "@types/prop-types" "^15.7.5"
+ "@types/react-is" "^16.7.1 || ^17.0.0"
+ prop-types "^15.8.1"
+ react-is "^18.2.0"
+
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -1528,6 +1722,11 @@
schema-utils "^3.0.0"
source-map "^0.7.3"
+"@popperjs/core@^2.11.6", "@popperjs/core@^2.4.0":
+ version "2.11.6"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
+ integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
+
"@radix-ui/popper@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/popper/-/popper-0.1.0.tgz#c387a38f31b7799e1ea0d2bb1ca0c91c2931b063"
@@ -2193,7 +2392,7 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed"
integrity sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw==
-"@types/prop-types@*":
+"@types/prop-types@*", "@types/prop-types@^15.7.5":
version "15.7.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
@@ -2220,6 +2419,20 @@
dependencies:
"@types/react" "*"
+"@types/react-is@^16.7.1 || ^17.0.0":
+ version "17.0.3"
+ resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a"
+ integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react-transition-group@^4.4.5":
+ version "4.4.5"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
+ integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==
+ dependencies:
+ "@types/react" "*"
+
"@types/react@*":
version "18.0.15"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe"
@@ -3254,7 +3467,7 @@ check-types@^11.1.1:
resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f"
integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==
-chokidar@^3.4.2, chokidar@^3.5.3:
+"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2, chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
@@ -3284,7 +3497,7 @@ cjs-module-lexer@^1.0.0:
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
-classnames@^2.3.1:
+classnames@^2.2.6, classnames@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
@@ -3305,6 +3518,11 @@ cliui@^7.0.2:
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"
+clsx@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
+ integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
+
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -3457,7 +3675,7 @@ content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
-convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
+convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
@@ -3732,7 +3950,7 @@ cssstyle@^2.3.0:
dependencies:
cssom "~0.3.6"
-csstype@^3.0.2, csstype@^3.0.4:
+csstype@^3.0.2, csstype@^3.0.4, csstype@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
@@ -3957,6 +4175,14 @@ dom-converter@^0.2.0:
dependencies:
utila "~0.4"
+dom-helpers@^5.0.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+ integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
dom-serializer@0:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@@ -4391,6 +4617,18 @@ eslint-plugin-testing-library@^5.0.1:
dependencies:
"@typescript-eslint/utils" "^5.13.0"
+eslint-plugin-unused-imports@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz#d8db8c4d0cfa0637a8b51ce3fd7d1b6bc3f08520"
+ integrity sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==
+ dependencies:
+ eslint-rule-composer "^0.3.0"
+
+eslint-rule-composer@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
+ integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
+
eslint-scope@5.1.1, eslint-scope@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@@ -4747,6 +4985,11 @@ find-cache-dir@^3.3.1:
make-dir "^3.0.2"
pkg-dir "^4.1.0"
+find-root@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
+ integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
+
find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
@@ -5093,7 +5336,7 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
-hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
+hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -5285,6 +5528,11 @@ immer@^9.0.7:
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc"
integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==
+immutable@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
+ integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
+
import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
@@ -7664,7 +7912,7 @@ prompts@^2.0.1, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.8.1:
+prop-types@^15.6.2, 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==
@@ -7742,6 +7990,11 @@ raw-body@2.5.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
+re-resizable@^6.9.9:
+ version "6.9.9"
+ resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.9.tgz#99e8b31c67a62115dc9c5394b7e55892265be216"
+ integrity sha512-l+MBlKZffv/SicxDySKEEh42hR6m5bAHfNu3Tvxks2c4Ah+ldnWjfnVRwxo/nxF27SsUsxDS0raAzFuJNKABXA==
+
react-app-polyfill@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz#95221e0a9bd259e5ca6b177c7bb1cb6768f68fd7"
@@ -7811,6 +8064,11 @@ react-error-overlay@^6.0.11:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
+react-icons@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703"
+ integrity sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==
+
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -7821,11 +8079,21 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-react-is@^18.0.0:
+react-is@^18.0.0, react-is@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+react-pro-sidebar@^0.7.1:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/react-pro-sidebar/-/react-pro-sidebar-0.7.1.tgz#0b5edca0809ff6a23bda188c5db370f880690ee7"
+ integrity sha512-Iy1X8ce4t5Vqz4CsyzjwokGUE3/IObgmYzS0ins7/2eWKle0SMUPaWdgMKFIVjtVrMr5vmjPbRicq8FxnVaf8A==
+ dependencies:
+ "@popperjs/core" "^2.4.0"
+ classnames "^2.2.6"
+ react-slidedown "^2.4.5"
+ resize-observer-polyfill "^1.5.1"
+
react-redux@^8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.2.tgz#bc2a304bb21e79c6808e3e47c50fe1caf62f7aad"
@@ -7898,6 +8166,23 @@ react-scripts@^5.0.1:
optionalDependencies:
fsevents "^2.3.2"
+react-slidedown@^2.4.5:
+ version "2.4.7"
+ resolved "https://registry.yarnpkg.com/react-slidedown/-/react-slidedown-2.4.7.tgz#c09e72bba8aac25018fd644ece041da771854589"
+ integrity sha512-HGDfrqo70r1WVE0DwrySPdCT27/2wcZaJYh5kOnmuPSCtjDDJrNkDdn4Ep/cma2VVfwupeAGhbc2pbrGThU6VQ==
+ dependencies:
+ tslib "^2.0.0"
+
+react-transition-group@^4.4.5:
+ version "4.4.5"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
+ integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ dom-helpers "^5.0.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+
react@^18.1.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@@ -8071,6 +8356,11 @@ reselect@^4.1.5:
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.6.tgz#19ca2d3d0b35373a74dc1c98692cdaffb6602656"
integrity sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ==
+resize-observer-polyfill@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
+ integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
+
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -8198,6 +8488,15 @@ sass-loader@^12.3.0:
klona "^2.0.4"
neo-async "^2.6.2"
+sass@^1.54.8:
+ version "1.54.8"
+ resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.8.tgz#4adef0dd86ea2b1e4074f551eeda4fc5f812a996"
+ integrity sha512-ib4JhLRRgbg6QVy6bsv5uJxnJMTS2soVcCp9Y88Extyy13A8vV0G1fAwujOzmNkFQbR3LvedudAMbtuNRPbQww==
+ dependencies:
+ chokidar ">=3.0.0 <4.0.0"
+ immutable "^4.0.0"
+ source-map-js ">=0.6.2 <2.0.0"
+
sax@~1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@@ -8437,7 +8736,7 @@ source-list-map@^2.0.0, source-list-map@^2.0.1:
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
-source-map-js@^1.0.1, source-map-js@^1.0.2:
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
@@ -8472,6 +8771,11 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+source-map@^0.5.7:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
+
source-map@^0.7.3:
version "0.7.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
@@ -8546,6 +8850,11 @@ stackframe@^1.3.4:
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
+stats.js@^0.17.0:
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/stats.js/-/stats.js-0.17.0.tgz#b1c3dc46d94498b578b7fd3985b81ace7131cc7d"
+ integrity sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==
+
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
@@ -8700,6 +9009,11 @@ stylehacks@^5.1.0:
browserslist "^4.16.6"
postcss-selector-parser "^6.0.4"
+stylis@4.0.13:
+ version "4.0.13"
+ resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
+ integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
+
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@@ -8969,7 +9283,7 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-tslib@^2.0.3, tslib@^2.1.0:
+tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==