Skip to content

Commit

Permalink
Move JR nav items into top nav bar
Browse files Browse the repository at this point in the history
I can never ever see the blue items menu even when looking for it--
partly because it's not in an expected place for navigation,
partly because it gets stretched super wide on non-small windows such
that it doesn't even look like a set of buttons.

Now they're in the nav bar (if a hunt ID is set; they don't appear
on the hunts index page).

Also improves contrast on the top nav bg a little and adds a bottom border
to the nav bar (in the same color as the vertical panes separator).

In small windows the menu appears in the old spot and  looks more or
less like the old one.
  • Loading branch information
aldeka committed Jan 9, 2024
1 parent acff0b3 commit a17a531
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 113 deletions.
30 changes: 27 additions & 3 deletions imports/client/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Container from "react-bootstrap/esm/Container";
import type { FallbackProps } from "react-error-boundary";
import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary";
import * as RRBS from "react-router-bootstrap";
import { useNavigate, Link } from "react-router-dom";
import { useNavigate, Link, useParams } from "react-router-dom";
import type { StackFrame } from "stacktrace-js";
import StackTrace from "stacktrace-js";
import styled, { css } from "styled-components";
Expand All @@ -32,12 +32,13 @@ import Loading from "./Loading";
import NotificationCenter from "./NotificationCenter";
import { NavBarHeight } from "./styling/constants";
import { mediaBreakpointDown } from "./styling/responsive";
import HuntNav from "./HuntNav";

const Breadcrumb = styled.nav`
display: flex;
align-items: center;
height: ${NavBarHeight};
flex: 1;
flex: 1 1 auto;
min-width: 0;
`;

Expand Down Expand Up @@ -99,6 +100,15 @@ const Brand = styled.img`
height: ${NavBarHeight};
`;

const HuntNavWrapper = styled.div`
${mediaBreakpointDown(
"sm",
css`
display: none;
`,
)}
`;

const ErrorFallback = ({
error,
clearError,
Expand Down Expand Up @@ -180,6 +190,7 @@ const ReactErrorBoundaryFallback = ({

const AppNavbar = () => {
const userId = useTracker(() => Meteor.userId()!, []);
const huntId = useParams<"huntId">().huntId!;

const displayName = useTracker(
() => Meteor.user()?.displayName ?? "<no name given>",
Expand Down Expand Up @@ -229,7 +240,15 @@ const AppNavbar = () => {
// correct amount of space in the top bar even if we haven't actually picked
// a nonempty source for it yet.
return (
<NavbarInset fixed="top" bg="light" variant="light" className="py-0">
<NavbarInset
fixed="top"
variant="light"
style={{
backgroundColor: "#f0f0f0",
borderBottom: "1px solid #6c757d",
}}
className="py-0"
>
<NavbarBrand className="p-0">
<Link to="/">
<Brand
Expand All @@ -240,6 +259,11 @@ const AppNavbar = () => {
</Link>
</NavbarBrand>
{breadcrumbsComponent}
{huntId && (
<HuntNavWrapper>
<HuntNav />
</HuntNavWrapper>
)}
<Nav className="ml-auto">
<Dropdown as={NavItem}>
<DropdownToggle id="profileDropdown" as={NavLink}>
Expand Down
216 changes: 216 additions & 0 deletions imports/client/components/HuntNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import { Meteor } from "meteor/meteor";
import { useTracker } from "meteor/react-meteor-data";
import { faBullhorn } from "@fortawesome/free-solid-svg-icons/faBullhorn";
import { faFaucet } from "@fortawesome/free-solid-svg-icons/faFaucet";
import { faMap } from "@fortawesome/free-solid-svg-icons/faMap";
import { faReceipt } from "@fortawesome/free-solid-svg-icons/faReceipt";
import { faUsers } from "@fortawesome/free-solid-svg-icons/faUsers";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react";
import Button from "react-bootstrap/Button";
import { useParams } from "react-router-dom";
import styled, { css } from "styled-components";
import Hunts from "../../lib/models/Hunts";
import { userMayWritePuzzlesForHunt } from "../../lib/permission_stubs";
import { mediaBreakpointDown } from "./styling/responsive";
import { Nav } from "react-bootstrap";

const HuntLinkWrapper = styled.div`
border-color: #cfcfcf;
border-style: solid;
border-width: 0 0 0 1px;
margin-left: 8px;
padding-left: 8px;
display: flex;
align-items: center;
justify-content: center;
max-height: 50px;
overflow-y: hidden;
${mediaBreakpointDown(
"lg",
css`
border-left-width: 0;
`,
)}
${mediaBreakpointDown(
"sm",
css`
margin-left: 0;
padding-left: 0;
flex: 1;
a {
border-radius: 0;
flex: 1;
}
`,
)}
`;

const JRLinkList = styled(Nav)`
border-color: #cfcfcf;
border-style: solid;
border-width: 0 1px 0 0;
margin-right: 8px;
padding-right: 8px;
flex: 1 1 auto;
display: flex;
justify-content: flex-end;
${mediaBreakpointDown(
"lg",
css`
border-right-width: 0;
`,
)}
${mediaBreakpointDown(
"sm",
css`
justify-content: space-between;
border: 1px solid #0d6efd;
margin-right: 0;
padding-right: 0;
`,
)}
`;

const StyledPuzzleListLinkAnchor = styled(Nav.Link)`
display: flex;
align-items: center;
justify-content: center;
max-height: 50px;
overflow: hidden;
${mediaBreakpointDown(
"lg",
css`
flex-direction: column;
padding-top: 0;
padding-bottom: 0;
`,
)}
${mediaBreakpointDown(
"sm",
css`
flex: 1;
&:hover {
background: #f0f0f0;
}
`,
)}
`;

const StyledPuzzleListLinkLabel = styled.span`
margin-left: 8px;
${mediaBreakpointDown(
"lg",
css`
margin-left: 0;
margin-top: 2px;
font-size: 0.8rem;
text-align: center;
`,
)}
${mediaBreakpointDown(
"md",
css`
display: none;
`,
)}
`;

const HuntHomeButtonContents = styled.div`
display: flex;
align-items: center;
justify-content: center;
${mediaBreakpointDown(
"lg",
css`
flex-direction: column;
font-size: 0.8rem;
`,
)}
`;

const MenuIcon = styled(FontAwesomeIcon)`
${mediaBreakpointDown(
"md",
css`
padding: 0.2rem;
`,
)}
`;

const HuntNav = () => {
const huntId = useParams<"huntId">().huntId!;
const hunt = useTracker(() => Hunts.findOne(huntId)!, [huntId]);
const { canUpdate } = useTracker(() => {
return {
canUpdate: userMayWritePuzzlesForHunt(Meteor.user(), hunt),
};
}, [hunt]);
if (huntId && hunt) {
const huntLink = hunt.homepageUrl && (
<HuntLinkWrapper>
<Button
as="a"
href={hunt.homepageUrl}
target="_blank"
rel="noopener noreferrer"
title="Open the hunt homepage"
>
<HuntHomeButtonContents>
<MenuIcon icon={faMap} />
<StyledPuzzleListLinkLabel>Hunt site</StyledPuzzleListLinkLabel>
</HuntHomeButtonContents>
</Button>
</HuntLinkWrapper>
);
return (
<JRLinkList>
<StyledPuzzleListLinkAnchor
href={`/hunts/${huntId}/announcements`}
title="Announcements"
>
<MenuIcon icon={faBullhorn} />
<StyledPuzzleListLinkLabel>Announcements</StyledPuzzleListLinkLabel>
</StyledPuzzleListLinkAnchor>

<StyledPuzzleListLinkAnchor
href={`/hunts/${huntId}/guesses`}
title="Guess queue"
>
<MenuIcon icon={faReceipt} />
<StyledPuzzleListLinkLabel>Guess queue</StyledPuzzleListLinkLabel>
</StyledPuzzleListLinkAnchor>

<StyledPuzzleListLinkAnchor
href={`/hunts/${huntId}/hunters`}
title="Hunters"
>
<MenuIcon icon={faUsers} />
<StyledPuzzleListLinkLabel>Hunters</StyledPuzzleListLinkLabel>
</StyledPuzzleListLinkAnchor>

{/* Show firehose link only to operators */}
{canUpdate && (
<StyledPuzzleListLinkAnchor
href={`/hunts/${huntId}/firehose`}
title="Firehose"
>
<MenuIcon icon={faFaucet} />
<StyledPuzzleListLinkLabel>Firehose</StyledPuzzleListLinkLabel>
</StyledPuzzleListLinkAnchor>
)}
{huntLink}
</JRLinkList>
);
}
return;
};

export default HuntNav;
Loading

0 comments on commit a17a531

Please sign in to comment.