Browse Source

Pop out menu from left on phone screens.

main
Titouan Rigoudy 4 years ago
parent
commit
c80f9a183b
7 changed files with 116 additions and 53 deletions
  1. +37
    -16
      src/components/Menu.tsx
  2. +14
    -6
      src/components/SearchableList.tsx
  3. +32
    -11
      src/components/SolsticeApp.tsx
  4. +4
    -1
      src/modules/room/RoomList.tsx
  5. +11
    -7
      src/modules/room/RoomsPane.tsx
  6. +3
    -1
      src/modules/user/UserList.tsx
  7. +15
    -11
      src/modules/user/UsersPane.tsx

+ 37
- 16
src/components/Menu.tsx View File

@ -14,21 +14,42 @@ const Link: FC<LinkProps> = ({ to, children }) => (
</NavLink> </NavLink>
); );
const Menu: FC = () => (
<header className="hidden md:flex p-4 items-center w-full overflow-hidden">
<h1
className={
"text-3xl font-bold text-yellow-800 bg-yellow-300 shadow " +
"mr-5 py-3 px-5 rounded-xl"
}
>
Solstice
</h1>
<nav className="flex items-center">
<Link to={roomListPath}>Rooms</Link>
<Link to={userListPath}>Users</Link>
</nav>
</header>
);
interface Props {
show: boolean;
onHide: () => void;
}
const Menu: FC<Props> = ({ show, onHide }) => {
let headerClass =
"absolute md:static flex flex-col md:flex-row items-center gap-5 p-4 " +
"w-80 min-h-full md:w-full md:min-h-0 overflow-hidden " +
"bg-yellow-100 border-r-2 border-yellow-400 ";
if (!show) {
headerClass += " -left-80 md:left-0";
}
return (
<header className={headerClass}>
<div className="flex items-center justify-between min-w-full md:min-w-0">
<h1
className={
"text-3xl font-bold text-yellow-800 bg-yellow-300 shadow " +
"mr-5 py-3 px-5 rounded-xl"
}
>
Solstice
</h1>
<button className="button md:hidden" onClick={onHide}>
Hide
</button>
</div>
<nav className="flex flex-col md:flex-row md:items-center gap-3">
<Link to={roomListPath}>Rooms</Link>
<Link to={userListPath}>Users</Link>
</nav>
</header>
);
};
export default Menu; export default Menu;

+ 14
- 6
src/components/SearchableList.tsx View File

@ -14,6 +14,8 @@ interface ItemProps<Item> {
interface ListProps<Item> { interface ListProps<Item> {
title: string; title: string;
onRefresh: ReactEventHandler; onRefresh: ReactEventHandler;
// TODO: Make required.
onShowMenu?: () => void;
component: FC<ItemProps<Item>>; component: FC<ItemProps<Item>>;
map: { [key: string]: Item }; map: { [key: string]: Item };
} }
@ -21,6 +23,7 @@ interface ListProps<Item> {
function SearchableList<Item>({ function SearchableList<Item>({
title, title,
onRefresh, onRefresh,
onShowMenu,
component, component,
map, map,
}: ListProps<Item>): ReactElement { }: ListProps<Item>): ReactElement {
@ -39,13 +42,18 @@ function SearchableList<Item>({
return ( return (
<div className="flex flex-col px-3 h-full"> <div className="flex flex-col px-3 h-full">
<div className="flex justify-between p-3">
<h1 className="pl-3 text-2xl font-bold text-yellow-800 text-center">
{title}
</h1>
<button onClick={onRefresh} className="button">
Refresh
<div className="flex p-3 gap-3 items-center">
<button onClick={onShowMenu} className="button md:hidden">
Menu
</button> </button>
<div className="flex-grow flex items-center gap-3 justify-between">
<h1 className="pl-3 text-2xl font-bold text-yellow-800 text-center">
{title}
</h1>
<button onClick={onRefresh} className="button">
Refresh
</button>
</div>
</div> </div>
<form className="flex gap-3 p-3"> <form className="flex gap-3 p-3">
<label htmlFor="filter" className="self-center"> <label htmlFor="filter" className="self-center">


+ 32
- 11
src/components/SolsticeApp.tsx View File

@ -1,4 +1,4 @@
import { FC } from "react";
import { FC, useState } from "react";
import { Switch, Redirect, Route, useLocation } from "react-router-dom"; import { Switch, Redirect, Route, useLocation } from "react-router-dom";
import { useAppSelector } from "app/hooks"; import { useAppSelector } from "app/hooks";
@ -11,22 +11,47 @@ import { userListPath } from "modules/user/paths";
import UsersPane from "modules/user/UsersPane"; import UsersPane from "modules/user/UsersPane";
import { selectSocket, SocketState } from "modules/socket/slice"; import { selectSocket, SocketState } from "modules/socket/slice";
const MainPane: FC = () => {
interface MainProps {
onShowMenu: () => void;
}
const MainPane: FC<MainProps> = ({ onShowMenu }) => {
return ( return (
<main className="flex-1 min-h-0 w-full"> <main className="flex-1 min-h-0 w-full">
<Switch> <Switch>
<Route path={roomListPath}> <Route path={roomListPath}>
<RoomsPane />
<RoomsPane onShowMenu={onShowMenu} />
</Route> </Route>
<Route path={userListPath}> <Route path={userListPath}>
<UsersPane />
<UsersPane onShowMenu={onShowMenu} />
</Route> </Route>
</Switch> </Switch>
</main> </main>
); );
}; };
const ConnectedApp: FC = ({ children }) => {
const InnerApp: FC<{}> = () => {
const [showMenu, setShowMenu] = useState(false);
return (
<div className="h-full w-full flex flex-col">
<Menu
show={showMenu}
onHide={() => {
setShowMenu(false);
}}
/>
<MainPane
onShowMenu={() => {
setShowMenu(true);
}}
/>
<Footer />
</div>
);
};
const ConnectedApp: FC<{}> = () => {
const socket = useAppSelector(selectSocket); const socket = useAppSelector(selectSocket);
const location = useLocation(); const location = useLocation();
@ -41,7 +66,7 @@ const ConnectedApp: FC = ({ children }) => {
); );
} }
return <div className="h-full w-full flex flex-col">{children}</div>;
return <InnerApp />;
}; };
const SolsticeApp = () => ( const SolsticeApp = () => (
@ -50,11 +75,7 @@ const SolsticeApp = () => (
<ConnectPage /> <ConnectPage />
</Route> </Route>
<Route path="/"> <Route path="/">
<ConnectedApp>
<Menu />
<MainPane />
<Footer />
</ConnectedApp>
<ConnectedApp />
</Route> </Route>
</Switch> </Switch>
); );


+ 4
- 1
src/modules/room/RoomList.tsx View File

@ -7,9 +7,10 @@ import { RoomMap, roomGetAll } from "modules/room/slice";
interface Props { interface Props {
rooms: RoomMap; rooms: RoomMap;
onShowMenu: () => void;
} }
const RoomList: FC<Props> = ({ rooms }) => {
const RoomList: FC<Props> = ({ rooms, onShowMenu }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const onRefresh: ReactEventHandler = (event) => { const onRefresh: ReactEventHandler = (event) => {
@ -17,9 +18,11 @@ const RoomList: FC<Props> = ({ rooms }) => {
dispatch(roomGetAll()); dispatch(roomGetAll());
}; };
// TODO: Move header out of SearchableList component.
return ( return (
<SearchableList <SearchableList
title="Chat Rooms" title="Chat Rooms"
onShowMenu={onShowMenu}
onRefresh={onRefresh} onRefresh={onRefresh}
component={RoomListEntry} component={RoomListEntry}
map={rooms} map={rooms}


+ 11
- 7
src/modules/room/RoomsPane.tsx View File

@ -28,11 +28,15 @@ const RoomChatPane: FC<ChatProps> = ({ loginUserName, roomName, room }) => {
return <RoomChat loginUserName={loginUserName} room={room} />; return <RoomChat loginUserName={loginUserName} room={room} />;
}; };
interface Props {
onShowMenu: () => void;
}
interface UrlParams { interface UrlParams {
roomId: string; roomId: string;
} }
const RoomsPaneInner: FC<{}> = () => {
const RoomsPaneInner: FC<Props> = ({ onShowMenu }) => {
const login = useAppSelector(selectLogin); const login = useAppSelector(selectLogin);
const rooms = useAppSelector(selectAllRooms); const rooms = useAppSelector(selectAllRooms);
const { roomId } = useParams<UrlParams>(); const { roomId } = useParams<UrlParams>();
@ -55,7 +59,7 @@ const RoomsPaneInner: FC<{}> = () => {
return ( return (
<div className="h-full w-full flex"> <div className="h-full w-full flex">
<div className={listClass}> <div className={listClass}>
<RoomList rooms={rooms} />
<RoomList rooms={rooms} onShowMenu={onShowMenu} />
</div> </div>
<div className="flex-1 overflow-auto"> <div className="flex-1 overflow-auto">
<RoomChatPane <RoomChatPane
@ -68,17 +72,17 @@ const RoomsPaneInner: FC<{}> = () => {
); );
}; };
const RoomsPane: FC<{}> = () => {
const RoomsPane: FC<Props> = ({ onShowMenu }) => {
const { path } = useRouteMatch(); const { path } = useRouteMatch();
const inner = <RoomsPaneInner onShowMenu={onShowMenu} />;
return ( return (
<Switch> <Switch>
<Route exact path={path}> <Route exact path={path}>
<RoomsPaneInner />
</Route>
<Route path={`${path}/:roomId`}>
<RoomsPaneInner />
{inner}
</Route> </Route>
<Route path={`${path}/:roomId`}>{inner}</Route>
</Switch> </Switch>
); );
}; };


+ 3
- 1
src/modules/user/UserList.tsx View File

@ -7,9 +7,10 @@ import UserListEntry from "modules/user/UserListEntry";
interface Props { interface Props {
users: UserMap; users: UserMap;
onShowMenu: () => void;
} }
const UserList: FC<Props> = ({ users }) => {
const UserList: FC<Props> = ({ users, onShowMenu }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const onRefresh: ReactEventHandler = (event) => { const onRefresh: ReactEventHandler = (event) => {
@ -21,6 +22,7 @@ const UserList: FC<Props> = ({ users }) => {
<SearchableList <SearchableList
title="Users" title="Users"
onRefresh={onRefresh} onRefresh={onRefresh}
onShowMenu={onShowMenu}
component={UserListEntry} component={UserListEntry}
map={users} map={users}
/> />


+ 15
- 11
src/modules/user/UsersPane.tsx View File

@ -8,14 +8,6 @@ import { UserMap, selectAllUsers } from "modules/user/slice";
import UserDetails from "modules/user/UserDetails"; import UserDetails from "modules/user/UserDetails";
import UserList from "modules/user/UserList"; import UserList from "modules/user/UserList";
interface Props {
users: UserMap;
}
interface UrlParams {
userId: string;
}
interface BannerProps { interface BannerProps {
title: string; title: string;
} }
@ -28,7 +20,15 @@ const UserInfoBanner: FC<BannerProps> = ({ title }) => {
); );
}; };
const UserInfoPane: FC<Props> = ({ users }) => {
interface InfoProps {
users: UserMap;
}
interface UrlParams {
userId: string;
}
const UserInfoPane: FC<InfoProps> = ({ users }) => {
const { userId } = useParams<UrlParams>(); const { userId } = useParams<UrlParams>();
if (userId === undefined) { if (userId === undefined) {
@ -44,14 +44,18 @@ const UserInfoPane: FC<Props> = ({ users }) => {
return <UserDetails user={user} />; return <UserDetails user={user} />;
}; };
const UsersPane: FC<{}> = () => {
interface Props {
onShowMenu: () => void;
}
const UsersPane: FC<Props> = ({ onShowMenu }) => {
const { path } = useRouteMatch(); const { path } = useRouteMatch();
const users = useAppSelector(selectAllUsers); const users = useAppSelector(selectAllUsers);
return ( return (
<div className="h-full w-full flex"> <div className="h-full w-full flex">
<div className="w-80"> <div className="w-80">
<UserList users={users} />
<UserList users={users} onShowMenu={onShowMenu} />
</div> </div>
<div className="flex-grow"> <div className="flex-grow">
<Switch> <Switch>


Loading…
Cancel
Save