diff --git a/client/package.json b/client/package.json index cfb07597e..5766e56c5 100644 --- a/client/package.json +++ b/client/package.json @@ -27,13 +27,10 @@ }, "dependencies": { "@babel/core": "7.1.0", - "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", "@fortawesome/fontawesome-free": "5.13.0", "@fullcalendar/core": "^6.0.0", "@fullcalendar/daygrid": "^6.0.0", "@fullcalendar/react": "^6.0.0", - "@mui/material": "^5.11.1", "@nivo/bar": "0.79.1", "@nivo/colors": "0.79.1", "@nivo/core": "0.79.0", diff --git a/client/src/app/App.tsx b/client/src/app/App.tsx index eab6b3ef7..0c3ffa3f7 100644 --- a/client/src/app/App.tsx +++ b/client/src/app/App.tsx @@ -1,4 +1,3 @@ -import { MaterialThemeProvider } from "components/layout/misc/MaterialThemeProvider"; import { LoadingScreen } from "components/layout/screens/LoadingScreen"; import { AllLUGPTStatsContextProvider } from "context/AllLUGPTStatsContext"; import { BannedContextProvider } from "context/BannedContext"; @@ -29,12 +28,10 @@ export default function App({ basename }: { basename: string }) { - - - - - - + + + + diff --git a/client/src/components/dashboard/DashboardHeader.tsx b/client/src/components/dashboard/DashboardHeader.tsx index 0d74554d6..8234b03df 100644 --- a/client/src/components/dashboard/DashboardHeader.tsx +++ b/client/src/components/dashboard/DashboardHeader.tsx @@ -1,26 +1,21 @@ -import Navbar from "components/nav/Navbar"; -import NavItem from "components/nav/NavItem"; import React from "react"; +import Navbar from "components/nav/Navbar"; export function DashboardHeader() { const navItems = [ - + Activity - , - + , + Calendar - , - + , + Your Profiles - , - + , + Global Activity - , + , ]; - return ( -
- {navItems} -
- ); + return {navItems}; } diff --git a/client/src/components/game/GPTHeader.tsx b/client/src/components/game/GPTHeader.tsx index 3cd47761f..de6b19ff4 100644 --- a/client/src/components/game/GPTHeader.tsx +++ b/client/src/components/game/GPTHeader.tsx @@ -1,41 +1,36 @@ -import Navbar from "components/nav/Navbar"; -import NavItem from "components/nav/NavItem"; -import { UserSettingsContext } from "context/UserSettingsContext"; import React, { useContext } from "react"; +import { UserSettingsContext } from "context/UserSettingsContext"; +import Navbar from "components/nav/Navbar"; export function GPTBottomNav({ baseUrl }: { baseUrl: string }) { const { settings } = useContext(UserSettingsContext); const navItems = [ - + Activity - , - + , + Songs - , - + , + Leaderboards - , - , + Quests - , + , ]; if (settings?.preferences.developerMode) { navItems.push( - + Developer Info - + ); } - return ( -
- {navItems} -
- ); + return {navItems}; } diff --git a/client/src/components/layout/LayoutHeaderContainer.tsx b/client/src/components/layout/LayoutHeaderContainer.tsx index 9a44fe821..011788b14 100644 --- a/client/src/components/layout/LayoutHeaderContainer.tsx +++ b/client/src/components/layout/LayoutHeaderContainer.tsx @@ -20,7 +20,7 @@ export default function UserHeaderContainer({
{children}
-
{footer}
+
{footer}
diff --git a/client/src/components/layout/misc/MaterialThemeProvider.tsx b/client/src/components/layout/misc/MaterialThemeProvider.tsx deleted file mode 100644 index 0a01486fc..000000000 --- a/client/src/components/layout/misc/MaterialThemeProvider.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ThemeProvider, createTheme } from "@mui/material/styles"; - -import { ColourConfig } from "lib/config"; -import React from "react"; -import { JustChildren } from "types/react"; - -const theme = createTheme({ - typography: { - fontFamily: "Poppins", - }, - palette: { - secondary: { - main: ColourConfig.primary, - }, - primary: { - main: ColourConfig.primary, - }, - }, - components: { - MuiButtonBase: { - defaultProps: { - disableRipple: true, - }, - }, - }, -}); - -export function MaterialThemeProvider({ children }: JustChildren) { - return {children}; -} diff --git a/client/src/components/layout/screens/SplashScreen.tsx b/client/src/components/layout/screens/SplashScreen.tsx index 6fe1e1f3b..c4a1c9d3a 100644 --- a/client/src/components/layout/screens/SplashScreen.tsx +++ b/client/src/components/layout/screens/SplashScreen.tsx @@ -1,5 +1,5 @@ -import { CircularProgress } from "@mui/material"; import React from "react"; +import Loading from "components/util/Loading"; import SplashImage from "../misc/SplashImage"; export function SplashScreen({ broke }: { broke: string }) { @@ -9,7 +9,7 @@ export function SplashScreen({ broke }: { broke: string }) { className="bg-body position-fixed inset-0 d-flex flex-column justify-content-center align-items-center" > - {!broke && } + {!broke && } {broke &&

{broke}

}
); diff --git a/client/src/components/nav/NavItem.tsx b/client/src/components/nav/NavItem.tsx deleted file mode 100644 index 875c4a6c5..000000000 --- a/client/src/components/nav/NavItem.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Tab } from "@mui/material"; -import React from "react"; -import { Link } from "react-router-dom"; - -export default function NavItem({ - to, - children, -}: { - to: string; - otherMatchingPaths?: Array; - children: string; -}) { - return ( - // @ts-expect-error Faulty types from MUI. The to={to} here is necessary - // and works perfectly fine, but the MUI types disagree that it exists. - {children}} - LinkComponent={Link} - to={to} - style={{ opacity: 1 }} - /> - ); -} diff --git a/client/src/components/nav/Navbar.tsx b/client/src/components/nav/Navbar.tsx index 6483bd9af..32a465ace 100644 --- a/client/src/components/nav/Navbar.tsx +++ b/client/src/components/nav/Navbar.tsx @@ -1,52 +1,353 @@ -import Tabs from "@mui/material/Tabs"; -import React, { useMemo } from "react"; -import { useLocation } from "react-router-dom"; +import Icon from "components/util/Icon"; +import { debounce } from "lodash"; +import React, { + cloneElement, + KeyboardEventHandler, + ReactElement, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { useLocation, Link, LinkProps } from "react-router-dom"; -export default function Navbar({ children }: { children: JSX.Element[] }) { - const links = children - .filter((e) => e.props.to) - .map((e) => { - const basePath = - e.props.to[e.props.to.length - 1] === "/" - ? e.props.to.substring(0, e.props.to.length - 1) - : e.props.to; +export interface NavbarItemProps extends LinkProps { + to: string; + children: string; + /** + * An array of other pathnames that should trigger an active item + */ + otherMatchingPaths?: Array; +} - if (e.props.otherMatchingPaths) { - return [basePath, ...e.props.otherMatchingPaths]; - } - return [basePath]; - }); +export type NavbarItem = ReactElement; +export interface NavbarProps { + children: ReactElement | ReactElement[]; +} + +export default function Navbar({ children }: NavbarProps) { const location = useLocation(); - const value = useMemo(() => { - const loc = location.pathname.split(/([#?]|\/$)/u)[0]; + const navRef = useRef(null); + const itemsListRef = useRef(null); - let lidx = 0; + const [mounted, setMounted] = useState(false); + const [indicatorStyle, setIndicatorStyle] = useState({ + left: 0, + width: 0, + }); + const [showScrollLeft, setShowScrollLeft] = useState(false); + const [showScrollRight, setShowScrollRight] = useState(false); + + const links = useMemo( + () => + Array.isArray(children) + ? children.map((el) => { + const to = el.props.to; + if (!to) { + return []; + } + const basePath = + to[to.length - 1] === "/" ? to.substring(0, to.length - 1) : to; + + if (el.props.otherMatchingPaths) { + return [basePath, ...el.props.otherMatchingPaths]; + } + return [basePath]; + }) + : children.props.to + ? [[children.props.to]] + : [[]], + [children] + ); + + const activeLocIndex = useMemo(() => { + const loc = location.pathname.split(/([#?]|\/$)/u)[0]; + let locIndex = 0; for (let i = 0; i < links.length; i++) { const matchingPaths = links[i]; - if (matchingPaths.some((path) => loc.startsWith(path))) { - lidx = i; + locIndex = i; // break; THIS IS DELIBERATE TO AVOID / matching everything } } + return locIndex; + }, [links, location]); - return lidx; - }, [location]); + const items = React.Children.map(children, (child, index) => { + if (!React.isValidElement(child)) { + return null; + } + if (process.env.NODE_ENV === "development") { + if (child.type === React.Fragment) { + console.error("The navbar doesn't accept React fragments; Pass an array instead"); + } + } + return cloneElement(child, { + key: child.key || index, + "aria-selected": index === activeLocIndex, + role: "tab", + draggable: false, + className: "tachi-navbar-item", + }); + }); + + const scrollButtons = ( + <> + {(showScrollLeft || showScrollRight) && ( + <> + + + + )} + + ); + + const nav = navRef.current; + const itemsList = itemsListRef.current; + const activeElement = itemsList?.children[activeLocIndex]; + + const updateIndicator = () => { + if (nav && activeElement) { + const navRect = nav.getBoundingClientRect(); + const activeElementRect = activeElement.getBoundingClientRect(); + + setIndicatorStyle({ + left: activeElementRect.left - navRect.left + nav.scrollLeft, + width: activeElementRect.width, + }); + } + }; + + const scrollActiveElementIntoView = debounce( + () => { + if (nav && itemsList && activeElement) { + if (itemsList.clientWidth > nav.clientWidth) { + const childRect = activeElement.getBoundingClientRect(); + const navRect = nav.getBoundingClientRect(); + + const left = childRect.left - navRect.left + nav.scrollLeft; + const right = left + childRect.width; + + if (left <= nav.scrollLeft || right >= nav.scrollLeft + nav.clientWidth) { + // ScrollIntoView is seemingly broken in Chromium so we will do it ourselves + requestAnimationFrame(() => { + if (left < nav.scrollLeft) { + nav.scrollTo({ left }); + } else if (right > nav.scrollLeft + nav.clientWidth) { + nav.scrollTo({ left: right - nav.clientWidth }); + } + }); + } + } + } + }, + 125, + { trailing: true } + ); + + const handleScrollButton = (right = false) => { + if (nav && itemsList) { + const scrollSize = Array.from(itemsList.children).reduceRight( + (accumulator: number, item, index) => { + if (accumulator + item.clientWidth > nav.clientWidth) { + if (index === 0) { + return nav.clientWidth; + } + } + return accumulator + item.clientWidth; + }, + 0 + ); + nav.scrollTo({ + left: (nav.scrollLeft += right ? scrollSize : -scrollSize), + }); + } + }; + + const handleKeyDown: KeyboardEventHandler = (event) => { + type Tab = HTMLAnchorElement | null; + + if (!itemsList) { + return; + } + + const currentFocus = itemsList.ownerDocument.activeElement; + + if (!currentFocus) { + return; + } + + const previous = currentFocus.previousElementSibling as Tab; + const next = currentFocus.nextElementSibling as Tab; + const first = itemsList.firstElementChild as Tab; + const last = itemsList.lastElementChild as Tab; + + switch (event.key) { + case "ArrowLeft": + event.preventDefault(); + if (currentFocus === itemsList.firstElementChild) { + last?.focus(); + } else { + previous?.focus(); + } + break; + case "ArrowRight": + event.preventDefault(); + if (currentFocus === itemsList.lastElementChild) { + first?.focus(); + } else { + next?.focus(); + } + break; + case "Home": + event.preventDefault(); + first?.focus(); + break; + case "End": + event.preventDefault(); + last?.focus(); + break; + default: + break; + } + }; + + useEffect(() => { + setMounted(true); + }, []); + + useEffect(() => { + updateIndicator(); + scrollActiveElementIntoView(); + }, [nav, itemsList, activeElement]); + + useEffect(() => { + if (!nav || !itemsList) { + return; + } + + const debouncedUpdateIndicator = debounce(updateIndicator, 75, { trailing: true }); + + const debounceIntersectOptions = [125, { leading: true }] as const; + const handleFirstIntersect: IntersectionObserverCallback = debounce( + (entries) => setShowScrollLeft(!entries[0].isIntersecting), + ...debounceIntersectOptions + ); + const handleLastIntersect: IntersectionObserverCallback = debounce( + (entries) => setShowScrollRight(!entries[0].isIntersecting), + ...debounceIntersectOptions + ); + + const observeChildren = () => { + if ( + itemsList.children.length > 1 && + itemsList.firstElementChild && + itemsList.lastElementChild + ) { + firstObserver.observe(itemsList.firstElementChild); + lastObserver.observe(itemsList.lastElementChild); + } + }; + + /** + * handles observations whenever mutations may happen + * + * I'm not convinced we need this but the previously used mui navbar uses a mutation observer + * so i thought it would be safe to do the same in case there's a chance a conditional item changes during render + */ + const handleMutation: MutationCallback = (mutations) => { + firstObserver.disconnect(); + lastObserver.disconnect(); + + mutations.forEach((mutation) => { + mutation.addedNodes.forEach((node) => { + resizeObserver.observe(node as Element); + }); + mutation.removedNodes.forEach((node) => { + resizeObserver.unobserve(node as Element); + }); + }); + + updateIndicator(); + observeChildren(); + }; + + const mutationObserver = new MutationObserver(handleMutation); + mutationObserver.observe(itemsList, { childList: true }); + + const resizeObserver = new ResizeObserver(() => { + debouncedUpdateIndicator(); + scrollActiveElementIntoView(); + }); + Array.from(itemsList.children).forEach((item) => resizeObserver.observe(item)); + resizeObserver.observe(nav); + + const observerOptions = { + root: nav, + threshold: 0.75, + }; + const firstObserver = new IntersectionObserver(handleFirstIntersect, observerOptions); + const lastObserver = new IntersectionObserver(handleLastIntersect, observerOptions); + observeChildren(); + + return () => { + mutationObserver.disconnect(); + resizeObserver.disconnect(); + firstObserver.disconnect(); + lastObserver.disconnect(); + }; + }, [activeElement, nav, itemsList]); return ( - - {children} - +
+ + {scrollButtons} +
); } + +/** + * Exposes a React Router Link component accepting otherMatchingPaths + */ +export const NavbarItem = React.forwardRef( + /* we don't want to pass otherMatchingPaths to the Link component + it is still seen by the navbar and processed accordingly */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ({ otherMatchingPaths, ...restProps }, ref) => +); + +Navbar.Item = NavbarItem; diff --git a/client/src/components/user/UGPTHeader.tsx b/client/src/components/user/UGPTHeader.tsx index a6b9f3b68..5bec4ae30 100644 --- a/client/src/components/user/UGPTHeader.tsx +++ b/client/src/components/user/UGPTHeader.tsx @@ -3,7 +3,6 @@ import { APIFetchV1 } from "util/api"; import { HumaniseError } from "util/humanise-error"; import { SendErrorToast, SendSuccessToast } from "util/toaster"; import Navbar from "components/nav/Navbar"; -import NavItem from "components/nav/NavItem"; import MiniTable from "components/tables/components/MiniTable"; import Divider from "components/util/Divider"; import React, { useContext } from "react"; @@ -204,52 +203,52 @@ export function UGPTBottomNav({ playtype: Playtype; }) { const navItems = [ - + Overview & Activity - , - + , + Scores - , - + , + Folders - , - + , + Sessions - , + , ]; const utilsName = GetGPTUtilsName(game, playtype, isRequestedUser); if (utilsName) { navItems.push( - + {utilsName} - + ); } if (isRequestedUser) { navItems.push( - + Rivals - , - + , + Goals & Quests - + ); } navItems.push( - + Leaderboard - + ); if (isRequestedUser) { navItems.push( - + Settings - + ); } diff --git a/client/src/components/user/UserHeader.tsx b/client/src/components/user/UserHeader.tsx index 420605961..de85f911c 100644 --- a/client/src/components/user/UserHeader.tsx +++ b/client/src/components/user/UserHeader.tsx @@ -1,7 +1,6 @@ import { APIFetchV1 } from "util/api"; import { FormatDate } from "util/time"; import Navbar from "components/nav/Navbar"; -import NavItem from "components/nav/NavItem"; import Divider from "components/util/Divider"; import ExternalLink from "components/util/ExternalLink"; import Icon from "components/util/Icon"; @@ -133,24 +132,24 @@ export function UserBottomNav({ baseUrl, reqUser }: { baseUrl: string; reqUser: const isRequestedUser = !!(user && user.id === reqUser.id); const navItems = [ - + Overview - , - + , + Games - , + , ]; if (isRequestedUser) { navItems.push( - + Service Integrations - + ); navItems.push( - + Imports - + ); // If mandates login, assume that we also use invite codes. @@ -158,24 +157,20 @@ export function UserBottomNav({ baseUrl, reqUser }: { baseUrl: string; reqUser: // querying the server, but I just don't care. if (ClientConfig.MANDATE_LOGIN) { navItems.push( - + Invites - + ); } navItems.push( - + Profile Settings - + ); } - return ( -
- {navItems} -
- ); + return {navItems}; } function StatusComponent({ reqUser }: { reqUser: UserDocument }) { diff --git a/client/src/components/util/Loading.tsx b/client/src/components/util/Loading.tsx index 59916236a..1d525685e 100644 --- a/client/src/components/util/Loading.tsx +++ b/client/src/components/util/Loading.tsx @@ -1,10 +1,10 @@ -import CircularProgress from "@mui/material/CircularProgress"; import React, { CSSProperties } from "react"; +import Spinner from "react-bootstrap/Spinner"; export default function Loading({ style }: { style?: CSSProperties }) { return (
- +
); } diff --git a/client/src/styles/bootstrap/_index.scss b/client/src/styles/bootstrap/_index.scss index 7969f0098..243086911 100644 --- a/client/src/styles/bootstrap/_index.scss +++ b/client/src/styles/bootstrap/_index.scss @@ -32,6 +32,7 @@ @import "bootstrap-scss/pagination"; @import "bootstrap-scss/modal"; @import "bootstrap-scss/close"; +@import "bootstrap-scss/spinners"; @import "customised-bootstrap"; @import "bootstrap-scss/utilities/api"; diff --git a/client/src/styles/bootstrap/customised-bootstrap/maps.scss b/client/src/styles/bootstrap/customised-bootstrap/maps.scss index 41348d52f..cb91b5dcd 100644 --- a/client/src/styles/bootstrap/customised-bootstrap/maps.scss +++ b/client/src/styles/bootstrap/customised-bootstrap/maps.scss @@ -20,8 +20,11 @@ $utilities: map-merge( 50: 50%, //75: 75%, 100: 100%, - //"half": 50%, - "full": 100%, + //half: 50%, + full: 100%, + min: min-content, + fit: fit-content, + max: max-content, auto: auto, ), ), @@ -43,8 +46,11 @@ $utilities: map-merge( //50: 50%, //75: 75%, 100: 100%, - //"half": 50%, - "full": 100%, + //half: 50%, + full: 100%, + min: min-content, + fit: fit-content, + max: max-content, auto: auto, ), ), @@ -161,7 +167,7 @@ $utilities: map-merge( "sm": var(--#{$prefix}box-shadow-sm), "md": var(--#{$prefix}box-shadow), "lg": var(--#{$prefix}box-shadow-lg), - //"inset": var(--#{$prefix}box-shadow-inset), + "inset": var(--#{$prefix}box-shadow-inset), "none": none, ), ), diff --git a/client/src/styles/components/_index.scss b/client/src/styles/components/_index.scss index e72660f29..194f7798c 100644 --- a/client/src/styles/components/_index.scss +++ b/client/src/styles/components/_index.scss @@ -3,4 +3,5 @@ @import "folder-minimap"; @import "table"; @import "timeline"; -@import "tooltip"; \ No newline at end of file +@import "tooltip"; +@import "navbar"; diff --git a/client/src/styles/components/navbar.scss b/client/src/styles/components/navbar.scss new file mode 100644 index 000000000..52ec7b5d4 --- /dev/null +++ b/client/src/styles/components/navbar.scss @@ -0,0 +1,70 @@ +.tachi-navbar { + display: inline-flex; + position: relative; + outline: none; + width: 100%; + background-color: var(--bs-secondary-bg); + overflow-x: auto; + overflow-y: hidden; + scroll-behavior: smooth; + @extend .scrollbar-hide; + + &-item { + padding: 1rem 2rem; + outline: none; + text-decoration: none; + text-wrap: nowrap; + user-select: none; + z-index: 20; + &:focus-visible { + text-decoration: underline; + @extend .text-primary; + } + } + + &-button { + %navbar-button { + position: absolute; + top: 0; + border: none; + height: 100%; + color: rgba(var(--bs-body-color-rgb), 0.8); + transition: opacity, ease-in-out, $transition-duration; + z-index: 40; + @extend .animate-fade-in; + &:hover, + &:focus { + color: rgba(var(--bs-body-color-rgb), 1); + } + } + + $navbar-button-gradient: rgba(var(--bs-secondary-bg-rgb), 0) 0%, + rgba(var(--bs-secondary-bg-rgb), 1) 60%; + + &-left { + @extend %navbar-button; + left: 0; + padding: 0 0.85rem 0 0.65rem; + background: linear-gradient(270deg, $navbar-button-gradient); + } + + &-right { + @extend %navbar-button; + right: 0; + padding: 0 0.65rem 0 0.85rem; + background: linear-gradient(90deg, $navbar-button-gradient); + } + } + + &-indicator { + position: absolute; + bottom: 2px; + height: 2px; + border-radius: var(--bs-border-radius); + background-color: var(--bs-primary); + transition-property: left, width; + transition-duration: $transition-duration-lg; + transition-timing-function: ease-in-out; + z-index: 10; + } +} diff --git a/client/src/styles/globals.scss b/client/src/styles/globals.scss index 41a3256dc..fe1a39f73 100644 --- a/client/src/styles/globals.scss +++ b/client/src/styles/globals.scss @@ -1,23 +1,27 @@ .nav-link { - @extend .rounded; + @extend .rounded; } .credits-easter-egg { - transition: text-decoration-color 120ms ease-in-out; - text-decoration: underline; - text-decoration-color: transparent; - &:hover { - text-decoration-color: var(--#{$prefix}body-color); - } + transition: text-decoration-color 120ms ease-in-out; + text-decoration: underline; + text-decoration-color: transparent; + &:hover { + text-decoration-color: var(--#{$prefix}body-color); + } +} + +a { + @extend .transition-color; } body, header { - @extend .transition-bg; - @extend .transition-color; + @extend .transition-bg; + @extend .transition-color; } code { - padding: #{$kbd-padding-y} #{$kbd-padding-x}; - border-radius: var(--#{$prefix}border-radius-sm); + padding: #{$kbd-padding-y} #{$kbd-padding-x}; + border-radius: var(--#{$prefix}border-radius-sm); } diff --git a/client/src/styles/legacy/tachi-custom/_custom.scss b/client/src/styles/legacy/tachi-custom/_custom.scss index 3c54962e7..5fa4d51c7 100644 --- a/client/src/styles/legacy/tachi-custom/_custom.scss +++ b/client/src/styles/legacy/tachi-custom/_custom.scss @@ -40,10 +40,3 @@ transition: opacity 0.3s; opacity: 0; } - -a:where(.MuiTab-root) { - @extend .transition-color; - &:hover { - color: var(--#{$prefix}link-hover-color) - } -} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c2e68829..d370b3077 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,12 +170,6 @@ importers: '@babel/core': specifier: 7.1.0 version: 7.1.0 - '@emotion/react': - specifier: ^11.10.5 - version: 11.10.5(@babel/core@7.1.0)(@types/react@17.0.13)(react@17.0.2) - '@emotion/styled': - specifier: ^11.10.5 - version: 11.10.5(@babel/core@7.1.0)(@emotion/react@11.10.5)(@types/react@17.0.13)(react@17.0.2) '@fortawesome/fontawesome-free': specifier: 5.13.0 version: 5.13.0 @@ -188,9 +182,6 @@ importers: '@fullcalendar/react': specifier: ^6.0.0 version: 6.0.0(@fullcalendar/core@6.0.0)(react-dom@17.0.2)(react@17.0.2) - '@mui/material': - specifier: ^5.11.1 - version: 5.11.1(@emotion/react@11.10.5)(@emotion/styled@11.10.5)(@types/react@17.0.13)(react-dom@17.0.2)(react@17.0.2) '@nivo/bar': specifier: 0.79.1 version: 0.79.1(@nivo/core@0.79.0)(prop-types@15.6.0)(react-dom@17.0.2)(react@17.0.2) @@ -2310,12 +2301,6 @@ packages: resolution: {integrity: sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==} dev: false - /@emotion/is-prop-valid@1.2.0: - resolution: {integrity: sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==} - dependencies: - '@emotion/memoize': 0.8.0 - dev: false - /@emotion/memoize@0.8.0: resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==} dev: false @@ -2359,31 +2344,6 @@ packages: resolution: {integrity: sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==} dev: false - /@emotion/styled@11.10.5(@babel/core@7.1.0)(@emotion/react@11.10.5)(@types/react@17.0.13)(react@17.0.2): - resolution: {integrity: sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw==} - peerDependencies: - '@babel/core': ^7.0.0 - '@emotion/react': ^11.0.0-rc.0 - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@babel/core': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/core': 7.1.0 - '@babel/runtime': 7.20.7 - '@emotion/babel-plugin': 11.10.5(@babel/core@7.1.0) - '@emotion/is-prop-valid': 1.2.0 - '@emotion/react': 11.10.5(@babel/core@7.1.0)(@types/react@17.0.13)(react@17.0.2) - '@emotion/serialize': 1.1.1 - '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@17.0.2) - '@emotion/utils': 1.2.0 - '@types/react': 17.0.13 - react: 17.0.2 - dev: false - /@emotion/unitless@0.8.0: resolution: {integrity: sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==} dev: false @@ -2630,164 +2590,6 @@ packages: dev: false optional: true - /@mui/base@5.0.0-alpha.111(@types/react@17.0.13)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-2wfIPpl97S4dPzD0QOM3UIzQ/EuXCYQvHmXxTpfKxev/cfkzOe7Ik/McoYUBbtM1bSOqH3W276R/L2LF9cyXqQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.20.7 - '@emotion/is-prop-valid': 1.2.0 - '@mui/types': 7.2.3(@types/react@17.0.13) - '@mui/utils': 5.11.1(react@17.0.2) - '@popperjs/core': 2.11.6 - '@types/react': 17.0.13 - clsx: 1.2.1 - prop-types: 15.8.1 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - react-is: 18.2.0 - dev: false - - /@mui/core-downloads-tracker@5.11.1: - resolution: {integrity: sha512-QVqVNlZ2K+LqUDE5kFgYd0r4KekR/dv2cNYbAutQWbfOA8VPVUVrDz0ELrEcoe8TjM/CwnsmGvaDh/YSNl/ALA==} - dev: false - - /@mui/material@5.11.1(@emotion/react@11.10.5)(@emotion/styled@11.10.5)(@types/react@17.0.13)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-yaZiXvcrl2vgUK+VO24780BWRgwdAMmAyuMVZnRTts1Yu0tWd6PjIYq2ZtaOlpj6/LbaSS+Q2kSfxYnDQ20CEQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.20.7 - '@emotion/react': 11.10.5(@babel/core@7.1.0)(@types/react@17.0.13)(react@17.0.2) - '@emotion/styled': 11.10.5(@babel/core@7.1.0)(@emotion/react@11.10.5)(@types/react@17.0.13)(react@17.0.2) - '@mui/base': 5.0.0-alpha.111(@types/react@17.0.13)(react-dom@17.0.2)(react@17.0.2) - '@mui/core-downloads-tracker': 5.11.1 - '@mui/system': 5.11.1(@emotion/react@11.10.5)(@emotion/styled@11.10.5)(@types/react@17.0.13)(react@17.0.2) - '@mui/types': 7.2.3(@types/react@17.0.13) - '@mui/utils': 5.11.1(react@17.0.2) - '@types/react': 17.0.13 - '@types/react-transition-group': 4.4.5 - clsx: 1.2.1 - csstype: 3.1.1 - prop-types: 15.8.1 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - react-is: 18.2.0 - react-transition-group: 4.4.5(react-dom@17.0.2)(react@17.0.2) - dev: false - - /@mui/private-theming@5.11.1(@types/react@17.0.13)(react@17.0.2): - resolution: {integrity: sha512-nnHg7kA5RwFRhy0wiDYe59sLCVGORpPypL1JcEdhv0+N0Zbmc2E/y4z2zqMRZ62MAEscpro7cQbvv244ThA84A==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.22.6 - '@mui/utils': 5.11.1(react@17.0.2) - '@types/react': 17.0.13 - prop-types: 15.8.1 - react: 17.0.2 - dev: false - - /@mui/styled-engine@5.11.0(@emotion/react@11.10.5)(@emotion/styled@11.10.5)(react@17.0.2): - resolution: {integrity: sha512-AF06K60Zc58qf0f7X+Y/QjaHaZq16znliLnGc9iVrV/+s8Ln/FCoeNuFvhlCbZZQ5WQcJvcy59zp0nXrklGGPQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.4.1 - '@emotion/styled': ^11.3.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - dependencies: - '@babel/runtime': 7.22.6 - '@emotion/cache': 11.10.5 - '@emotion/react': 11.10.5(@babel/core@7.1.0)(@types/react@17.0.13)(react@17.0.2) - '@emotion/styled': 11.10.5(@babel/core@7.1.0)(@emotion/react@11.10.5)(@types/react@17.0.13)(react@17.0.2) - csstype: 3.1.1 - prop-types: 15.8.1 - react: 17.0.2 - dev: false - - /@mui/system@5.11.1(@emotion/react@11.10.5)(@emotion/styled@11.10.5)(@types/react@17.0.13)(react@17.0.2): - resolution: {integrity: sha512-BEA2S0hay8n8CcZftkeAVsi0nsb5ZjdnZRCahv5lX7QJYwDjO4ucJ6lnvxHe2v/9Te1LLjTO7ojxu/qM6CE5Cg==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.20.7 - '@emotion/react': 11.10.5(@babel/core@7.1.0)(@types/react@17.0.13)(react@17.0.2) - '@emotion/styled': 11.10.5(@babel/core@7.1.0)(@emotion/react@11.10.5)(@types/react@17.0.13)(react@17.0.2) - '@mui/private-theming': 5.11.1(@types/react@17.0.13)(react@17.0.2) - '@mui/styled-engine': 5.11.0(@emotion/react@11.10.5)(@emotion/styled@11.10.5)(react@17.0.2) - '@mui/types': 7.2.3(@types/react@17.0.13) - '@mui/utils': 5.11.1(react@17.0.2) - '@types/react': 17.0.13 - clsx: 1.2.1 - csstype: 3.1.1 - prop-types: 15.8.1 - react: 17.0.2 - dev: false - - /@mui/types@7.2.3(@types/react@17.0.13): - resolution: {integrity: sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==} - peerDependencies: - '@types/react': '*' - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@types/react': 17.0.13 - dev: false - - /@mui/utils@5.11.1(react@17.0.2): - resolution: {integrity: sha512-lMAPgIJoil8V9ZxsMbEflMsvZmWcHbRVMc4JDY9jPO9V4welpF43h/O267b1RqlcRnC5MEbVQV605GYkTZY29Q==} - engines: {node: '>=12.0.0'} - peerDependencies: - react: ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.20.7 - '@types/prop-types': 15.7.5 - '@types/react-is': 17.0.3 - prop-types: 15.8.1 - react: 17.0.2 - react-is: 18.2.0 - dev: false - /@nivo/annotations@0.79.1(@nivo/core@0.79.0)(prop-types@15.6.0)(react-dom@17.0.2)(react@17.0.2): resolution: {integrity: sha512-lYso9Luu0maSDtIufwvyVt2+Wue7R9Fh3CIjuRDmNR72UjAgAVEcCar27Fy865UXGsj2hRJZ7KY/1s6kT3gu/w==} peerDependencies: @@ -3620,12 +3422,6 @@ packages: '@types/react': 17.0.13 dev: false - /@types/react-is@17.0.3: - resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} - dependencies: - '@types/react': 17.0.13 - dev: false - /@types/react-router-bootstrap@0.26.1: resolution: {integrity: sha512-EdgZn/PmSA8+I0RW6WyDUgBvkVXc8AmbbgOJ8WJfec1R/V06X++OrIOzwzdgYl4g3uXvEB6oEIxzcjx1/8Opqw==} dependencies: @@ -4594,11 +4390,6 @@ packages: engines: {node: '>=0.8'} dev: false - /clsx@1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} - dev: false - /cluster-key-slot@1.1.0: resolution: {integrity: sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==} engines: {node: '>=0.10.0'} @@ -9066,10 +8857,6 @@ packages: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: false - /react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - dev: false - /react-lifecycles-compat@3.0.4: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} dev: false