From cef99cba7143f5f601a1ac18c958f0bb390ec6a9 Mon Sep 17 00:00:00 2001 From: Brent Clark Date: Wed, 15 Mar 2023 22:45:55 -0500 Subject: [PATCH] Add left nav (#4705) * Automated translation update ( bf08a04cda131e731dc4e8732e2bcf64167d9a7f ) * Add Layout component wip * finish layout implementation * convert header to left nav * better nav item spacing * return source files to original glory * lint fix * Remove height limit on login screen * fix top spacing on 3-panel layout --------- Co-authored-by: github-actions Co-authored-by: Brent Clark --- .../ThreePaneLayout/ThreePaneLayout.css | 1 + .../ThreePaneLayout/ThreePaneLayout.tsx | 5 +- webclient/src/components/index.ts | 1 - webclient/src/containers/Account/Account.tsx | 5 +- webclient/src/containers/App/AppShell.tsx | 3 - webclient/src/containers/Decks/Decks.tsx | 5 +- webclient/src/containers/Game/Game.tsx | 5 +- .../src/containers/Initialize/Initialize.tsx | 39 ++-- webclient/src/containers/Layout/Layout.css | 31 +++ webclient/src/containers/Layout/Layout.tsx | 39 ++++ .../Layout/LeftNav.css} | 61 +++--- .../Layout/LeftNav.tsx} | 65 +++--- .../Header => containers/Layout}/logo.png | Bin webclient/src/containers/Login/Login.tsx | 201 +++++++++--------- webclient/src/containers/Player/Player.tsx | 5 +- webclient/src/containers/Room/Room.tsx | 5 +- webclient/src/containers/Server/Server.tsx | 5 +- .../containers/Unsupported/Unsupported.tsx | 5 +- 18 files changed, 280 insertions(+), 201 deletions(-) create mode 100644 webclient/src/containers/Layout/Layout.css create mode 100644 webclient/src/containers/Layout/Layout.tsx rename webclient/src/{components/Header/Header.css => containers/Layout/LeftNav.css} (59%) rename webclient/src/{components/Header/Header.tsx => containers/Layout/LeftNav.tsx} (73%) rename webclient/src/{components/Header => containers/Layout}/logo.png (100%) diff --git a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.css b/webclient/src/components/ThreePaneLayout/ThreePaneLayout.css index d11fe5d7..f439dee5 100644 --- a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.css +++ b/webclient/src/components/ThreePaneLayout/ThreePaneLayout.css @@ -33,4 +33,5 @@ .three-pane-layout .grid-main__bottom.fixedHeight { height: 50%; overflow: visible; + padding: 0 0 16px; } diff --git a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx b/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx index 06610b16..2862cc0d 100644 --- a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx +++ b/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx @@ -1,5 +1,4 @@ -// eslint-disable-next-line -import React, { Component, CElement } from "react"; +import { Component, CElement } from "react"; import { connect } from 'react-redux'; import Grid from '@mui/material/Grid'; import Hidden from '@mui/material/Hidden'; @@ -12,7 +11,7 @@ class ThreePaneLayout extends Component { render() { return (
- + { }; return ( -
+
@@ -96,7 +97,7 @@ const Account = (props: AccountProps) => {
-
+ ) } diff --git a/webclient/src/containers/App/AppShell.tsx b/webclient/src/containers/App/AppShell.tsx index 3e9916c9..c28d2006 100644 --- a/webclient/src/containers/App/AppShell.tsx +++ b/webclient/src/containers/App/AppShell.tsx @@ -3,7 +3,6 @@ import { Provider } from 'react-redux'; import { MemoryRouter as Router } from 'react-router-dom'; import CssBaseline from '@mui/material/CssBaseline'; import { store } from 'store'; -import { Header } from 'components'; import Routes from './AppShellRoutes'; import FeatureDetection from './FeatureDetection'; @@ -29,8 +28,6 @@ class AppShell extends Component {
-
- diff --git a/webclient/src/containers/Decks/Decks.tsx b/webclient/src/containers/Decks/Decks.tsx index cff762cf..190196dc 100644 --- a/webclient/src/containers/Decks/Decks.tsx +++ b/webclient/src/containers/Decks/Decks.tsx @@ -2,16 +2,17 @@ import React, { Component } from "react"; import { AuthGuard } from 'components/index'; +import Layout from 'containers/Layout/Layout'; import './Decks.css'; class Decks extends Component { render() { return ( -
+ "Decks" -
+ ) } } diff --git a/webclient/src/containers/Game/Game.tsx b/webclient/src/containers/Game/Game.tsx index bdfeb171..694ffb46 100644 --- a/webclient/src/containers/Game/Game.tsx +++ b/webclient/src/containers/Game/Game.tsx @@ -2,16 +2,17 @@ import React, { Component } from "react"; import { AuthGuard } from 'components'; +import Layout from 'containers/Layout/Layout'; import './Game.css'; class Game extends Component { render() { return ( -
+ "Game" -
+ ) } } diff --git a/webclient/src/containers/Initialize/Initialize.tsx b/webclient/src/containers/Initialize/Initialize.tsx index 0024f44e..bc777f06 100644 --- a/webclient/src/containers/Initialize/Initialize.tsx +++ b/webclient/src/containers/Initialize/Initialize.tsx @@ -8,6 +8,7 @@ import Typography from '@mui/material/Typography'; import { Images } from 'images'; import { ServerSelectors } from 'store'; import { RouteEnum } from 'types'; +import Layout from 'containers/Layout/Layout'; import './Initialize.css'; @@ -35,25 +36,27 @@ const Initialize = ({ initialized }: InitializeProps) => { return initialized ? : ( - -
- logo - { t('InitializeContainer.title') } - - - - -
+ + +
+ logo + { t('InitializeContainer.title') } + + + + +
-
-
-
-
-
-
-
-
- +
+
+
+
+
+
+
+
+ + ); } diff --git a/webclient/src/containers/Layout/Layout.css b/webclient/src/containers/Layout/Layout.css new file mode 100644 index 00000000..a98cba47 --- /dev/null +++ b/webclient/src/containers/Layout/Layout.css @@ -0,0 +1,31 @@ +.layout { + height: 100%; + max-height: 100%; + width: 100%; + max-width: 100%; + display: flex; + flex-flow: row nowrap; + overflow: hidden; +} + +.layout--no-height-limit { + height: initial; + max-height: initial; +} + +.bottom-bar__container { + background: #555; + height: 50px; + width: 100%; +} + +.page__body { + flex: 1; + max-height: calc(100% - 50px); +} + +.page { + display: flex; + flex-flow: column; + width: 100%; +} diff --git a/webclient/src/containers/Layout/Layout.tsx b/webclient/src/containers/Layout/Layout.tsx new file mode 100644 index 00000000..de5d05c8 --- /dev/null +++ b/webclient/src/containers/Layout/Layout.tsx @@ -0,0 +1,39 @@ +import LeftNav from './LeftNav'; + +import './Layout.css' + +function Layout(props:LayoutProps) { + const { children, className, showNav = true, noHeightLimit = false } = props; + const containerClasses = ['layout'] + if (noHeightLimit === true) { + containerClasses.push('layout--no-height-limit') + } + + return ( +
+ {showNav && } +
+
+ {children} +
+ {showNav && } +
+
+ ) +} + +function BottomBar(props) { + return ( +
+
+ ) +} + +interface LayoutProps { + showNav?: boolean; + children: any; + className?: string; + noHeightLimit?: boolean +} + +export default Layout; diff --git a/webclient/src/components/Header/Header.css b/webclient/src/containers/Layout/LeftNav.css similarity index 59% rename from webclient/src/components/Header/Header.css rename to webclient/src/containers/Layout/LeftNav.css index ead7f73e..85c851d2 100644 --- a/webclient/src/components/Header/Header.css +++ b/webclient/src/containers/Layout/LeftNav.css @@ -1,31 +1,34 @@ -.Header { +.LeftNav__container { + background: #7033DB; + width: 100px; + min-width: 100px; + height: 100%; } -.Header__logo { +.LeftNav__logo { display: flex; align-items: center; + justify-content: center; + padding: 16px 0; } -.Header__logo a { +.LeftNav__logo a { line-height: 1; } -.Header__logo img { +.LeftNav__logo img { height: 32px; } -.Header-content { - display: flex; - align-items: center; - width: 100%; +.LeftNav-content { color: white; } -.Header-serverDetails { +.LeftNav-serverDetails { font-size: 12px; } -.Header-server__indicator { +.LeftNav-server__indicator { display: inline-block; height: 12px; width: 12px; @@ -35,46 +38,43 @@ margin-left: 10px; } -.Header-nav { - width: 100%; - display: flex; - justify-content: space-between; +.LeftNav-nav { } -.Header-nav__links { - width: 100%; +.LeftNav-nav__links { display: flex; - padding-left: 50px; + flex-flow: column; align-items: center; + gap: 16px; } -.Header-nav__link { +.LeftNav-nav__link { position: relative; height: 100%; } -.Header-nav__link:hover { +.LeftNav-nav__link:hover { background: rgba(0, 0, 0, .125); } -.Header-nav__link:hover .Header-nav__link-menu { +.LeftNav-nav__link:hover .LeftNav-nav__link-menu { display: block; } -.Header-nav__link-btn { +.LeftNav-nav__link-btn { display: flex; height: 100%; width: 100%; align-items: center; - padding: 5px 10px; + padding: 5px 20px; font-weight: bold; } -.Header-nav__link-btn__icon { +.LeftNav-nav__link-btn__icon { margin-left: 5px; } -.Header-nav__link-menu { +.LeftNav-nav__link-menu { display: none; position: absolute; bottom: 0; @@ -82,13 +82,14 @@ min-width: 150px; background: #3f51b5; box-shadow: 1px 1px 2px 0px black; + z-index: 1; } -.Header-nav__link-menu__item { +.LeftNav-nav__link-menu__item { padding: 0 !important; } -.Header-nav__link-menu__btn { +.LeftNav-nav__link-menu__btn { padding: 6px 16px; width: 100%; color: white; @@ -96,15 +97,16 @@ justify-content: space-between; } -.Header-nav__actions { +.LeftNav-nav__actions { display: flex; + justify-content: center; } -.Header-nav__action { +.LeftNav-nav__action { } -.Header-nav__action button { +.LeftNav-nav__action button { color: white; } @@ -124,4 +126,3 @@ .temp-chip > div { cursor: inherit; } - diff --git a/webclient/src/components/Header/Header.tsx b/webclient/src/containers/Layout/LeftNav.tsx similarity index 73% rename from webclient/src/components/Header/Header.tsx rename to webclient/src/containers/Layout/LeftNav.tsx index 4aef8ebc..a81eb6ce 100644 --- a/webclient/src/components/Header/Header.tsx +++ b/webclient/src/containers/Layout/LeftNav.tsx @@ -1,11 +1,9 @@ import React, { useState, useEffect } from 'react'; import { connect } from 'react-redux'; import { NavLink, useNavigate, generatePath } from 'react-router-dom'; -import AppBar from '@mui/material/AppBar'; import IconButton from '@mui/material/IconButton'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; -import Toolbar from '@mui/material/Toolbar'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import CloseIcon from '@mui/icons-material/Close'; import MailOutlineRoundedIcon from '@mui/icons-material/MailOutline'; @@ -18,11 +16,11 @@ import { Images } from 'images'; import { RoomsSelectors, ServerSelectors } from 'store'; import { Room, RouteEnum, User } from 'types'; -import './Header.css'; +import './LeftNav.css'; -const Header = ({ joinedRooms, serverState, user }: HeaderProps) => { +const LeftNav = ({ joinedRooms, serverState, user }: LeftNavProps) => { const navigate = useNavigate(); - const [state, setState] = useState({ + const [state, setState] = useState({ anchorEl: null, showCardImportDialog: false, options: [], @@ -73,23 +71,23 @@ const Header = ({ joinedRooms, serverState, user }: HeaderProps) => { } return ( - - -
+
+
+
logo { AuthenticationService.isConnected(serverState) && ( - + ) }
{ AuthenticationService.isConnected(serverState) && ( -
-
); } -interface HeaderProps { +interface LeftNavProps { serverState: number; server: string; user: User; joinedRooms: Room[]; + showNav?: boolean; } -interface HeaderState { +interface LeftNavState { anchorEl: Element; showCardImportDialog: boolean; options: string[]; @@ -193,4 +192,4 @@ const mapStateToProps = state => ({ joinedRooms: RoomsSelectors.getJoinedRooms(state), }); -export default connect(mapStateToProps)(Header); +export default connect(mapStateToProps)(LeftNav); diff --git a/webclient/src/components/Header/logo.png b/webclient/src/containers/Layout/logo.png similarity index 100% rename from webclient/src/components/Header/logo.png rename to webclient/src/containers/Layout/logo.png diff --git a/webclient/src/containers/Login/Login.tsx b/webclient/src/containers/Login/Login.tsx index 659e2e16..fe008375 100644 --- a/webclient/src/containers/Login/Login.tsx +++ b/webclient/src/containers/Login/Login.tsx @@ -16,6 +16,7 @@ import { Images } from 'images'; import { HostDTO, serverProps } from 'services'; import { RouteEnum, WebSocketConnectOptions, getHostPort } from 'types'; import { ServerSelectors, ServerTypes } from 'store'; +import Layout from 'containers/Layout/Layout'; import './Login.css'; import { useToast } from 'components/Toast'; @@ -225,120 +226,122 @@ const Login = ({ state, description, connectOptions }: LoginProps) => { }; return ( - - { isConnected && } + + + { isConnected && } -
- -
-
- logo - COCKATRICE -
- { t('LoginContainer.header.title') } - { t('LoginContainer.header.subtitle') } -
- -
- - { - showDescription() && ( - - {description} - - ) - } - -
-
- { t('LoginContainer.footer.registerPrompt') } - +
+ +
+
+ logo + COCKATRICE +
+ { t('LoginContainer.header.title') } + { t('LoginContainer.header.subtitle') } +
+
- - { t('LoginContainer.footer.credit') } - { new Date().getUTCFullYear() } - { - serverProps.REACT_APP_VERSION && ( - - { t('LoginContainer.footer.version') }: { serverProps.REACT_APP_VERSION } - + showDescription() && ( + + {description} + ) } -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Stock Player - 1mrlee -
+
+
+ { t('LoginContainer.footer.registerPrompt') } +
-
-
- Stock Player - CyberX -
-
-
-
- Stock Player - Gamer69 -
+ + { t('LoginContainer.footer.credit') } - { new Date().getUTCFullYear() } + + + { + serverProps.REACT_APP_VERSION && ( + + { t('LoginContainer.footer.version') }: { serverProps.REACT_APP_VERSION } + + ) + } + +
+
- { /**/} -

{ t('LoginContainer.content.subtitle1') }

-

{ t('LoginContainer.content.subtitle2') }

-
- -
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Stock Player + 1mrlee +
+
+
+
+ Stock Player + CyberX +
+
+
+
+ Stock Player + Gamer69 +
+
+
+ { /**/} +

{ t('LoginContainer.content.subtitle1') }

+

{ t('LoginContainer.content.subtitle2') }

+
+
+ +
- + - + - + - - + + + ); } diff --git a/webclient/src/containers/Player/Player.tsx b/webclient/src/containers/Player/Player.tsx index d57367f7..74feff8b 100644 --- a/webclient/src/containers/Player/Player.tsx +++ b/webclient/src/containers/Player/Player.tsx @@ -1,15 +1,16 @@ // eslint-disable-next-line import React, { Component } from "react"; +import Layout from 'containers/Layout/Layout'; import { AuthGuard } from 'components'; class Player extends Component { render() { return ( -
+ "Player" -
+ ) } } diff --git a/webclient/src/containers/Room/Room.tsx b/webclient/src/containers/Room/Room.tsx index a0c81703..c73930d7 100644 --- a/webclient/src/containers/Room/Room.tsx +++ b/webclient/src/containers/Room/Room.tsx @@ -9,6 +9,7 @@ import { RoomsService } from 'api'; import { ScrollToBottomOnChanges, ThreePaneLayout, UserDisplay, VirtualList, AuthGuard } from 'components'; import { RoomsStateMessages, RoomsStateRooms, JoinedRooms, RoomsSelectors, RoomsTypes } from 'store'; import { RouteEnum } from 'types'; +import Layout from 'containers/Layout/Layout'; import OpenGames from './OpenGames'; import Messages from './Messages'; @@ -40,7 +41,7 @@ const Room = (props) => { } return ( -
+
@@ -84,7 +85,7 @@ const Room = (props) => { )} />
-
+ ); } diff --git a/webclient/src/containers/Server/Server.tsx b/webclient/src/containers/Server/Server.tsx index 88a7b74e..7ed5e246 100644 --- a/webclient/src/containers/Server/Server.tsx +++ b/webclient/src/containers/Server/Server.tsx @@ -11,6 +11,7 @@ import { useReduxEffect } from 'hooks'; import { RoomsSelectors, RoomsTypes, ServerSelectors } from 'store'; import { Room, RouteEnum, User } from 'types'; import Rooms from './Rooms'; +import Layout from 'containers/Layout/Layout'; import './Server.css'; @@ -23,7 +24,7 @@ const Server = ({ message, rooms, joinedRooms, users }: ServerProps) => { }, RoomsTypes.JOIN_ROOM, []); return ( -
+ { )} /> -
+ ); } diff --git a/webclient/src/containers/Unsupported/Unsupported.tsx b/webclient/src/containers/Unsupported/Unsupported.tsx index 7e979a9d..b666da12 100644 --- a/webclient/src/containers/Unsupported/Unsupported.tsx +++ b/webclient/src/containers/Unsupported/Unsupported.tsx @@ -2,6 +2,7 @@ import { connect } from 'react-redux'; import { useTranslation } from 'react-i18next'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; +import Layout from 'containers/Layout/Layout'; import './Unsupported.css'; @@ -9,7 +10,7 @@ const Unsupported = () => { const { t } = useTranslation(); return ( -
+
{ t('UnsupportedContainer.title') } @@ -18,7 +19,7 @@ const Unsupported = () => { { t('UnsupportedContainer.subtitle2') } -
+
); };