[UNSTABLE] welp, navbars gone

pull/338/head
MrChristofferson 2017-07-04 09:20:51 -04:00
parent f1e29dc39f
commit 26ccce2dab
9 changed files with 167 additions and 345 deletions

View File

@ -1,5 +1,9 @@
/* Styles used throughout the frontend */
body {
background: $gray;
}
.ticker-list::-webkit-scrollbar {
display: none;
}
@ -27,9 +31,7 @@ input[type=time] {
}
.all-content-wrapper {
padding: 3rem 3rem 0rem 3rem;
margin-left: auto;
margin-right: auto;
margin: 0 auto;
width: 100%;
max-width: 160rem;
animation: page-transition 0.2s ease-in-out;

View File

@ -1,151 +1,8 @@
.nav-wrapper {
box-shadow: 0 4px 10px $translucent;
background: $dark_gray;
background: palegoldenrod;
}
nav {
background-color: $dark_gray;
color: $light_gray;
font-size: 1.2rem;
letter-spacing: .15rem;
text-transform: uppercase;
display: flex;
height: 100%;
align-items: center;
justify-content: space-between;
padding: 1rem 2rem 0.6rem;
max-width: 140rem;
margin: 3rem auto 0;
.nav-sync {
margin-right: 2rem !important;
}
.mobile-menu-extras {
display: none;
}
.mobile-only {
display: none;
}
.page-name {
display: none;
}
.right-nav-content {
align-items: center;
display: flex;
flex: 1;
justify-content: flex-end;
}
.links {
.fa {
display: none;
}
}
ul {
display: flex;
justify-content: space-around;
}
button {
margin: 0 !important;
font-size: 1.2rem !important;
padding: 0.5rem 1rem;
}
a {
white-space: nowrap;
padding: 1.8rem 1rem 2rem;
color: $gray !important;
position: relative;
transition: all 0.6s ease;
&:hover {
color: $white !important;
font-weight: bold;
transition: all 0.6s ease;
}
&.active {
color: $white !important;
font-weight: bold;
transition: all 0.4s ease;
&:before {
content: "";
background: $dark_gray;
position: absolute;
bottom: -3px;
left: 0;
right: 0;
height: 3px;
z-index: 1;
animation: slide-up 0.4s ease forwards;
}
&:after {
content: "";
background: $white;
position: absolute;
bottom: -3px;
left: 0;
right: 0;
height: 3px;
}
}
}
.nav-dropdown {
cursor: pointer;
position: relative;
padding: 1.4rem;
&:hover,
&:active {
.nav-dropdown-content {
opacity: 1;
transition: all 0.2s ease-in-out;
pointer-events: all;
}
}
.name {
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
max-width: 16rem;
display: inline-block;
}
.nav-dropdown-content {
top: 4rem;
transition: all 0.2s ease-in-out;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
position: absolute;
display: block;
background: #434343;
padding: 0 2.6rem 2.6rem;
right: 0;
z-index: 1;
opacity: 0;
pointer-events: none;
&:hover {
opacity: 1;
transition: all 0.2s ease-in-out;
pointer-events: all;
}
i {
padding-right: 1rem;
}
ul {
display: flex;
flex-direction: column;
width: 20rem;
li {
padding: 1rem;
width: 22rem;
}
}
.version-links {
position: absolute;
font-size: 1rem;
background: #000;
left: 0;
right: 0;
bottom: 0;
text-align: center;
padding: 0.8rem;
a {
text-decoration: underline !important;
}
}
}
}
max-width: 160rem;
padding: 1rem 4.4rem;
}

View File

@ -2,7 +2,7 @@ import * as React from "react";
import { connect } from "react-redux";
import { HardwareSettings } from "./components/hardware_settings";
import { FarmbotOsSettings } from "./components/farmbot_os_settings";
import { Page, Col } from "../ui/index";
import { Page, Col, Row } from "../ui";
import { mapStateToProps } from "./state_to_props";
import { Props } from "./interfaces";
@ -11,22 +11,26 @@ export class Devices extends React.Component<Props, {}> {
render() {
if (this.props.auth) {
return <Page className="devices">
<Col xs={12} sm={6}>
<FarmbotOsSettings
account={this.props.deviceAccount}
dispatch={this.props.dispatch}
bot={this.props.bot}
auth={this.props.auth} />
</Col>
<Col xs={12} sm={6}>
<HardwareSettings
controlPanelState={this.props.bot.controlPanelState}
dispatch={this.props.dispatch}
bot={this.props.bot} />
</Col>
<Row>
<Col xs={12} sm={6}>
<FarmbotOsSettings
account={this.props.deviceAccount}
dispatch={this.props.dispatch}
bot={this.props.bot}
auth={this.props.auth}
/>
</Col>
<Col xs={12} sm={6}>
<HardwareSettings
controlPanelState={this.props.bot.controlPanelState}
dispatch={this.props.dispatch}
bot={this.props.bot}
/>
</Col>
</Row>
</Page>;
} else {
throw new Error("Log in first");
}
}
};
}

View File

@ -0,0 +1,48 @@
import * as React from "react";
import { Link } from "react-router";
import { AdditonalMenuProps } from "./interfaces";
import { t } from "i18next";
export let AdditionalMenu = ({ user, onClick }: AdditonalMenuProps) => {
if (!user) {
return <span></span>;
} else {
let hasName = user && user.body.name;
let firstName = hasName ? `${hasName.split(" ")[0]}` : "▾";
return <div className="nav-dropdown">
<div className="nav-dropdown-content">
<ul>
<li>
<Link to="/app/account">
<i className="fa fa-cog"></i>
{t("Account Settings")}
</Link>
</li>
<li>
<a href="https://software.farmbot.io/docs/the-farmbot-web-app"
target="_blank">
<i className="fa fa-file-text-o"></i>{t("Documentation")}
</a>
</li>
<li>
<a onClick={onClick}>
<i className="fa fa-sign-out"></i>
{t("Logout")}
</a>
</li>
</ul>
<div className="version-links">
<span>{t("Application")}:
<a
href="https://github.com/FarmBot/Farmbot-Web-API"
target="_blank"
>
{(process.env.SHORT_REVISION || "NONE").slice(0, 8)}
</a>
</span>
</div>
</div>
</div>;
}
};

View File

@ -1,201 +1,45 @@
import * as React from "react";
import { Link } from "react-router";
import { DropDownProps, NavBarState, NavBarProps } from "./interfaces";
import { NavBarState, NavBarProps } from "./interfaces";
import { EStopButton } from "../devices/components/e_stop_btn";
import { t } from "i18next";
import { Session } from "../session";
import { Markdown } from "../ui";
import { Markdown, Row, Col } from "../ui";
import * as moment from "moment";
import { SyncButton } from "./sync_button";
import { history } from "../history";
import { updatePageInfo } from "../util";
let DropDown = ({ user, onClick }: DropDownProps) => {
// Just checking if user is logged in, otherwise nothing is returned.
if (!user) {
return <span></span>;
} else {
// Displaying the user's name in the top right of the screen if available.
let hasName = user && user.body.name;
let fullName = hasName ? `${hasName}` : "";
let firstName = fullName.split(" ")[0] + " ▾";
// The bit shown while hovering over username in top right of screen.
return <div className="nav-dropdown">
<span className="name">
{firstName}
</span>
<div className="nav-dropdown-content">
<ul>
<li>
<Link to="/app/account">
<i className="fa fa-cog"></i>
{t("Account Settings")}
</Link>
</li>
<li>
<a href="https://software.farmbot.io/docs/the-farmbot-web-app"
target="_blank">
<i className="fa fa-file-text-o"></i>{t("Documentation")}
</a>
</li>
<li>
<a onClick={onClick}>
<i className="fa fa-sign-out"></i>
{t("Logout")}
</a>
</li>
</ul>
<div className="version-links">
<span>{t("Application")}:
<a href="https://github.com/FarmBot/Farmbot-Web-API"
target="_blank">
{(process.env.SHORT_REVISION || "NONE").slice(0, 8)}
</a>
</span>
</div>
</div>
</div>;
}
};
// Easier way to keep track of links in the navbar.
let links = [
{ name: "Farm Designer", icon: "leaf", url: "/app/designer" },
{ name: "Controls", icon: "keyboard-o", url: "/app/controls" },
{ name: "Device", icon: "cog", url: "/app/device" },
{ name: "Sequences", icon: "server", url: "/app/sequences" },
{ name: "Regimens", icon: "calendar-check-o", url: "/app/regimens" },
{ name: "Tools", icon: "wrench", url: "/app/tools" },
{ name: "Farmware", icon: "crosshairs", url: "/app/farmware" }
];
export class NavBar extends React.Component<NavBarProps, NavBarState> {
state: NavBarState = { mobileNavExpanded: false, tickerExpanded: false };
toggleMobileNav = () => {
let { mobileNavExpanded } = this.state;
/** Don't let user scroll when nav is open */
this.setState({ mobileNavExpanded: !mobileNavExpanded });
}
toggleTicker = () => {
let { tickerExpanded } = this.state;
this.setState({ tickerExpanded: !tickerExpanded });
}
logout = () => Session.clear(true);
render() {
// Class for toggling the left sliding menu on mobile and tablets.
let mobileMenuClass = this.state.mobileNavExpanded ? "expanded" : "";
// Class for toggling the black bar, top of the screen containing logs.
let tickerClass = this.state.tickerExpanded ? "expanded" : "";
// The way our app is laid out, we'll pretty much always want this bit.
let pageName = history.getCurrentLocation().pathname.split("/")[2] || "";
// Change document meta title on every route change.
updatePageInfo(pageName);
let { toggleMobileNav, toggleTicker, logout } = this;
let user = this.props.user;
return <div className="nav-wrapper">
<nav role="navigation">
<button
className="mobile-and-tablet-only fb-button"
onClick={toggleMobileNav}>
<i className="fa fa-bars"></i>
</button>
<span className="page-name">{pageName}</span>
<div className={`links ${mobileMenuClass}`}>
<ul>
{links.map(link => {
return <li key={link.url}>
<Link to={link.url}
activeClassName="active">
<i className={`fa fa-${link.icon}`} />
{link.name}
</Link>
</li>;
})}
</ul>
{/** TODO: Getting the links from the desktop dropdown to the
mobile slide-out menu involves gnarly (and probably mobile-
incompatible) CSS. I'll look into this one. -CV */}
<ul className="mobile-menu-extras">
<li>
<Link to="/app/account">
<i className="fa fa-cog"></i>{t("Account Settings")}
</Link>
</li>
<li>
<a href="https://software.farmbot.io/docs/the-farmbot-web-app"
target="_blank">
<i className="fa fa-file-text-o"></i>{t("Documentation")}
</a>
</li>
<li>
<a onClick={logout}>
<i className="fa fa-sign-out"></i>
{t("Logout")}
</a>
</li>
</ul>
<div className="version-links mobile-only">
{t("Frontend")}:
<a href="https://github.com/FarmBot/farmbot-web-frontend"
target="_blank">
{/** SHORT_REVISION is the last frontend commit. */}
{process.env.SHORT_REVISION}
</a>
</div>
</div>
<div className={`ticker-list ${tickerClass}`} onClick={toggleTicker}>
{this.props.logs.map((log, index) => {
let isFiltered = log.message.toLowerCase().includes("filtered");
let time = moment.unix(log.created_at).local().format("h:mm a");
if (!isFiltered) {
return <div key={index} className="status-ticker-wrapper">
<div className={`saucer ${log.meta.type}`} />
<label className="status-ticker-message">
<Markdown>
{log.message || "Loading"}
</Markdown>
</label>
<label className="status-ticker-created-at">
{time}
</label>
</div>;
}
})}
</div>
{/** Everything to the right of the navigation links. */}
<div className="right-nav-content">
<SyncButton
bot={this.props.bot}
user={this.props.user}
dispatch={this.props.dispatch}
/>
<EStopButton
bot={this.props.bot}
user={this.props.user}
/>
<DropDown
onClick={logout}
user={user}
/>
</div>
{/** The darkish opaque backdrop when mobile/tablet is open */}
<div className={`underlay ${mobileMenuClass}`}
onClick={toggleMobileNav}></div>
</nav>
<Row>
<Col xs={12}>
<nav role="navigation">
<span className="page-name visible-xs-inline-block">
{pageName}
</span>
<SyncButton
bot={this.props.bot}
user={this.props.user}
dispatch={this.props.dispatch}
/>
<EStopButton
bot={this.props.bot}
user={this.props.user}
/>
</nav>
</Col>
</Row>
</div>;
}
}

View File

@ -9,7 +9,7 @@ export interface NavButtonProps {
onClick?: () => void;
}
export interface DropDownProps {
export interface AdditonalMenuProps {
user: TaggedUser | undefined;
onClick?: () => void;
}

44
src/nav/links.tsx 100644
View File

@ -0,0 +1,44 @@
import * as React from "react";
import { Link } from "react-router";
import { t } from "i18next";
let links = [
{ name: "Farm Designer", icon: "leaf", url: "/app/designer" },
{ name: "Controls", icon: "keyboard-o", url: "/app/controls" },
{ name: "Device", icon: "cog", url: "/app/device" },
{ name: "Sequences", icon: "server", url: "/app/sequences" },
{ name: "Regimens", icon: "calendar-check-o", url: "/app/regimens" },
{ name: "Tools", icon: "wrench", url: "/app/tools" },
{ name: "Farmware", icon: "crosshairs", url: "/app/farmware" },
{ name: "Account", icon: "cog", url: "/app/account" },
{ name: "Logout", icon: "sign-out", url: "" }
];
export let NavLinks = () => {
<div className="links">
{links.map(link => {
return <Link
to={link.url}
activeClassName="active"
key={link.url}
>
<i className={`fa fa-${link.icon}`} />
{link.name}
</Link>;
})}
<a href="https://software.farmbot.io/docs/the-farmbot-web-app"
target="_blank">
<i className="fa fa-file-text-o"></i>{t("Documentation")}
</a>
<div className="version-links">
{t("Frontend")}:
<a
href="https://github.com/FarmBot/farmbot-web-frontend"
target="_blank"
>
{/** SHORT_REVISION is the last frontend commit. */}
{process.env.SHORT_REVISION}
</a>
</div>
</div>;
};

View File

@ -33,9 +33,7 @@ export function SyncButton({ user, bot, dispatch }: NavButtonProps) {
let text = TEXT_MAPPING[sync_status] || "DISCONNECTED";
return <button
className={`nav-sync ${color} fb-button`}
onClick={() => {
dispatch(sync());
}}>
onClick={() => dispatch(sync())}>
{text}
</button>;
};
}

View File

@ -0,0 +1,25 @@
import * as React from "react";
import { Markdown } from "./ui/index";
import * as moment from "moment";
export function TickerList(props: any) {
<div className="ticker-list">
{props.logs.map((log, index) => {
let isFiltered = log.message.toLowerCase().includes("filtered");
let time = moment.unix(log.created_at).local().format("h:mm a");
if (!isFiltered) {
return <div key={index} className="status-ticker-wrapper">
<div className={`saucer ${log.meta.type}`} />
<label className="status-ticker-message">
<Markdown>
{log.message || "Loading"}
</Markdown>
</label>
<label className="status-ticker-created-at">
{time}
</label>
</div>;
}
})}
</div>;
}