farmware style updates

pull/338/head
MrChristofferson 2017-07-07 05:49:03 -05:00
parent a6e0b06e44
commit 7a7aef9a2d
9 changed files with 230 additions and 231 deletions

View File

@ -30,6 +30,12 @@ input[type=time] {
color: #fff;
}
fieldset {
button {
margin: 0.6rem 0 0 0.6rem;
}
}
.all-content-wrapper {
margin: 0 auto;
padding: 3rem 3rem 0;

View File

@ -1,54 +1,53 @@
.image-flipper {
margin-top: 2rem;
position: relative;
position: relative;
}
.image-flipper-image {
max-width: 100%;
margin: auto;
max-height: 650px;
max-width: 100%;
margin: auto;
max-height: 650px;
}
.image-flipper-left {
position: absolute;
left: 0;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
&:hover {
background-color: rgba(0, 0, 0, 0.7);
}
position: absolute;
left: 0;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
&:hover {
background-color: rgba(0, 0, 0, 0.7);
}
}
.image-flipper-right {
position: absolute;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
&:hover {
background-color: rgba(0, 0, 0, 0.7);
}
position: absolute;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
&:hover {
background-color: rgba(0, 0, 0, 0.7);
}
}
.is-loaded-false {
display: none;
display: none;
}
.is-loaded-true {
display: block;
display: block;
}
.no-flipper-image-container {
p {
position: absolute;
text-shadow: 0 0 25px rgba(0, 0, 0, 1), 0 0 25px rgba(0, 0, 0, 1);
color: $white;
font-size: 1.8rem;
text-align: center;
padding: 10% 10rem 0;
line-height: 2.4rem;
left: 0;
right: 0;
}
}
p {
position: absolute;
text-shadow: 0 0 25px rgba(0, 0, 0, 1), 0 0 25px rgba(0, 0, 0, 1);
color: $white;
font-size: 1.8rem;
text-align: center;
padding: 10% 10rem 0;
line-height: 2.4rem;
left: 0;
right: 0;
}
}

View File

@ -25,32 +25,4 @@
margin-right: 1rem;
position: relative;
}
}
.weed-detector-meta {
background: $dark_gray;
margin-left: -14px;
margin-right: -14px;
padding: 0.3rem 2rem 0;
margin-bottom: -2rem;
margin-top: 20px;
label {
font-weight: normal;
margin-right: 1rem;
color: $white;
}
span {
font-weight: bold;
color: $white;
}
.created-at {
display: inline-block;
}
.meta-coordinates {
float: right;
display: flex;
.coordinate:not(:last-of-type) {
margin-right: 1.8rem;
}
}
}

View File

@ -18,6 +18,7 @@
.widget-wrapper {
box-shadow: 0px 0px 10px $gray;
margin-bottom: 3rem;
position: relative;
}
.widget-header {
@ -57,13 +58,6 @@
}
}
.widget-body {
background: $off_white;
border-color: $light_gray;
color: $dark_gray;
padding: 1.25rem;
}
.help-icon:hover .help-text,
.help-icon:active .help-text {
opacity: 100;
@ -71,4 +65,19 @@
max-height: 200px;
transition: 0.5s;
transition-delay: 0.5s;
}
.widget-body {
background: $off_white;
border-color: $light_gray;
color: $dark_gray;
padding: 1.25rem;
}
.widget-footer {
background: $dark_gray;
padding: 0.5rem 1rem 0;
* {
color: $white;
}
}

View File

@ -1,4 +1,5 @@
import * as React from "react";
import * as _ from "lodash";
import { t } from "i18next";
import { devices } from "../device";
import { FWProps, FWState } from "./interfaces";
@ -13,7 +14,6 @@ import {
Col
} from "../ui";
import { betterCompact } from "../util";
import * as _ from "lodash";
export class FarmwarePanel extends React.Component<FWProps, Partial<FWState>> {
constructor() {
@ -78,70 +78,75 @@ export class FarmwarePanel extends React.Component<FWProps, Partial<FWState>> {
}
render() {
return <Widget className="farmware-widget">
<WidgetHeader title="Farmware" helpText={ToolTips.FARMWARE}>
</WidgetHeader>
<WidgetBody>
<MustBeOnline fallback="Not available when FarmBot is offline."
status={this.props.syncStatus}
lockOpen={process.env.NODE_ENV !== "production"}>
<Row>
<fieldset>
<Col xs={12}>
<input type="url"
placeholder={"https://...."}
value={this.state.packageUrl || ""}
onChange={(e) => {
this.setState({ packageUrl: e.currentTarget.value });
}}
/>
</Col>
<Col xs={12}>
<button
className="fb-button green"
onClick={this.install}
>
{t("Install")}
</button>
</Col>
</fieldset>
</Row>
<Row>
<Col xs={12}>
<DeprecatedFBSelect list={this.fwList()}
onChange={(x) => {
let selectedFarmware = x.value;
if (_.isString(selectedFarmware)) {
this.setState({ selectedFarmware });
} else {
throw new Error(`Bad farmware UUID: ${x.value}`);
}
}}
placeholder="Installed Farmware Packages" />
</Col>
<Col xs={12}>
<button
className="fb-button red"
onClick={this.remove}
>
{t("Remove")}
</button>
<button
className="fb-button yellow"
onClick={this.update}
>
{t("Update")}
</button>
<button
className="fb-button green"
onClick={this.run}
>
{t("Run")}
</button>
</Col>
</Row>
</MustBeOnline>
</WidgetBody>
</Widget>;
return (
<Widget className="farmware-widget">
<WidgetHeader title="Farmware" helpText={ToolTips.FARMWARE} />
<WidgetBody>
<MustBeOnline
fallback="Not available when FarmBot is offline."
status={this.props.syncStatus}
lockOpen={process.env.NODE_ENV !== "production"}
>
<Row>
<fieldset>
<Col xs={12}>
<input type="url"
placeholder={"https://...."}
value={this.state.packageUrl || ""}
onChange={(e) => {
this.setState({ packageUrl: e.currentTarget.value });
}}
/>
</Col>
<Col xs={12}>
<button
className="fb-button green"
onClick={this.install}
>
{t("Install")}
</button>
</Col>
</fieldset>
</Row>
<Row>
<fieldset>
<Col xs={12}>
<DeprecatedFBSelect list={this.fwList()}
onChange={(x) => {
let selectedFarmware = x.value;
if (_.isString(selectedFarmware)) {
this.setState({ selectedFarmware });
} else {
throw new Error(`Bad farmware UUID: ${x.value}`);
}
}}
placeholder="Installed Farmware Packages" />
</Col>
<Col xs={12}>
<button
className="fb-button red"
onClick={this.remove}
>
{t("Remove")}
</button>
<button
className="fb-button yellow"
onClick={this.update}
>
{t("Update")}
</button>
<button
className="fb-button green"
onClick={this.run}
>
{t("Run")}
</button>
</Col>
</fieldset>
</Row>
</MustBeOnline>
</WidgetBody>
</Widget>
);
}
}

View File

@ -19,12 +19,14 @@ export class FarmwarePage extends React.Component<FarmwareProps, {}> {
<Photos
dispatch={this.props.dispatch}
images={this.props.images}
currentImage={this.props.currentImage} />
currentImage={this.props.currentImage}
/>
</Col>
<Col xs={12} sm={4}>
<Col xs={12} sm={5}>
<FarmwarePanel
syncStatus={this.props.syncStatus}
farmwares={this.props.farmwares} />
farmwares={this.props.farmwares}
/>
</Col>
</Row>
<Row>
@ -42,7 +44,8 @@ export class FarmwarePage extends React.Component<FarmwareProps, {}> {
V_LO={envGet("CAMERA_CALIBRATION_V_LO", this.props.env)}
H_HI={envGet("CAMERA_CALIBRATION_H_HI", this.props.env)}
S_HI={envGet("CAMERA_CALIBRATION_S_HI", this.props.env)}
V_HI={envGet("CAMERA_CALIBRATION_V_HI", this.props.env)} />
V_HI={envGet("CAMERA_CALIBRATION_V_HI", this.props.env)}
/>
</Col>
<Col xs={12} sm={5} smOffset={1}>
<WeedDetector {...this.props} />

View File

@ -1,4 +1,6 @@
import * as React from "react";
import * as _ from "lodash";
import * as moment from "moment";
import { t } from "i18next";
import { success, error } from "farmbot-toastr";
import { Widget, WidgetHeader, WidgetBody } from "../ui/index";
@ -7,6 +9,28 @@ import { PhotosProps } from "./interfaces";
import { devices } from "../device";
import { ToolTips } from "../constants";
import { selectImage } from "../images/actions";
import { WidgetFooter } from "../ui/widget_footer";
import { safeStringFetch } from "../util";
interface MetaInfoProps {
/** Default conversion is `attr_name ==> Attr Name`.
* Setting a label property will over ride it to a differrent value.
*/
label?: string;
attr: string;
obj: any; /** Really, it's OK here! See safeStringFetch */
}
function MetaInfo({ obj, attr, label }: MetaInfoProps) {
let top = label || _.startCase(attr.split("_").join());
let bottom = safeStringFetch(obj, attr);
return (
<div>
<label>{top}:&nbsp;&nbsp;</label>
<span>{bottom || "unknown"}</span>
</div>
);
}
export class Photos extends React.Component<PhotosProps, {}> {
@ -16,22 +40,50 @@ export class Photos extends React.Component<PhotosProps, {}> {
devices.current.takePhoto().then(ok, no);
}
metaDatas() {
let i = this.props.currentImage;
if (i) {
let { meta } = i.body;
return Object.keys(meta).sort().map(function (key, index) {
return <MetaInfo key={index} attr={key} obj={meta} />;
});
} else {
return <MetaInfo attr={t("image")} obj={{ image: t("No meta data.") }} />;
}
}
render() {
return <Widget className="photos-widget">
<WidgetHeader helpText={ToolTips.PHOTOS} title={"Photos"}>
<button
className="fb-button gray"
onClick={this.takePhoto}
>
{t("Take Photo")}
</button>
</WidgetHeader>
<WidgetBody>
<ImageFlipper
onFlip={(id) => { this.props.dispatch(selectImage(id)) }}
currentImage={this.props.currentImage}
images={this.props.images} />
</WidgetBody>
</Widget>
let image = this.props.currentImage;
return (
<Widget className="photos-widget">
<WidgetHeader helpText={ToolTips.PHOTOS} title={"Photos"}>
<button
className="fb-button gray"
onClick={this.takePhoto}
>
{t("Take Photo")}
</button>
</WidgetHeader>
<WidgetBody>
<ImageFlipper
onFlip={id => { this.props.dispatch(selectImage(id)); }}
currentImage={this.props.currentImage}
images={this.props.images}
/>
</WidgetBody>
<WidgetFooter>
{/** Separated from <MetaInfo /> for stylistic purposes. */}
{image ?
<div>
<label>{t("Created At")}</label>
<span>
{moment(image.body.created_at).format("MMMM Do, YYYY h:mma")}
</span>
</div>
: ""}
{this.metaDatas()}
</WidgetFooter>
</Widget>
);
}
}

View File

@ -1,14 +1,14 @@
import { ImageFlipperProps, ImageFlipperState } from "./interfaces";
import * as React from "react";
import { safeStringFetch } from "../util";
import { t } from "i18next";
import * as moment from "moment";
import * as _ from "lodash";
import * as moment from "moment";
import { ImageFlipperProps, ImageFlipperState } from "./interfaces";
import { safeStringFetch } from "../util";
export const PLACEHOLDER_FARMBOT = "/placeholder_farmbot.jpg";
export class ImageFlipper
extends React.Component<ImageFlipperProps, Partial<ImageFlipperState>> {
export class ImageFlipper extends
React.Component<ImageFlipperProps, Partial<ImageFlipperState>> {
state: ImageFlipperState = { isLoaded: false };
@ -24,12 +24,14 @@ export class ImageFlipper
<p>{t(`Image loading (try refreshing)`)}</p>
<img
className="image-flipper-image"
src={PLACEHOLDER_FARMBOT} />
src={PLACEHOLDER_FARMBOT}
/>
</div>)}
<img
onLoad={() => this.setState({ isLoaded: true })}
className={`image-flipper-image is-loaded-${this.state.isLoaded}`}
src={url} />
src={url}
/>
</div>;
} else {
return <div className="no-flipper-image-container">
@ -37,23 +39,12 @@ export class ImageFlipper
Once you do, they will show up here.`)}</p>
<img
className="image-flipper-image"
src={PLACEHOLDER_FARMBOT} />
src={PLACEHOLDER_FARMBOT}
/>
</div>;
}
}
metaDatas() {
let i = this.props.currentImage;
if (i) {
let { meta } = i.body;
return Object.keys(meta).sort().map(function (key, index) {
return <MetaInfo key={index} attr={key} obj={meta} />;
});
} else {
return <MetaInfo attr={"image"} obj={{ image: "No meta data." }} />;
}
}
go = (increment: -1 | 1) => () => {
let { images, currentImage } = this.props;
let uuids = images.map(x => x.uuid);
@ -66,59 +57,22 @@ export class ImageFlipper
render() {
let image = this.imageJSX();
let i = this.props.currentImage;
return <div>
<div className="row">
<div className="col-sm-12">
<div className="image-flipper">
{image}
<button
onClick={this.go(1)}
className="image-flipper-left fb-button"
>
{t("Prev")}
</button>
<button
onClick={this.go(-1)}
className="image-flipper-right fb-button"
>
{t("Next")}
</button>
</div>
</div>
return (
<div className="image-flipper">
{image}
<button
onClick={this.go(1)}
className="image-flipper-left fb-button"
>
{t("Prev")}
</button>
<button
onClick={this.go(-1)}
className="image-flipper-right fb-button"
>
{t("Next")}
</button>
</div>
<div className="weed-detector-meta">
{/** Separated from <MetaInfo /> for stylistic purposes. */}
{i ?
<div className="created-at">
<label>{t("Created At")}</label>
<span>
{moment(i.body.created_at).format("MMMM Do, YYYY h:mma")}
</span>
</div>
: ""}
<div className="meta-coordinates">
{this.metaDatas()}
</div>
</div>
</div>;
);
}
}
interface MetaInfoProps {
/** Default conversion is `attr_name ==> Attr Name`.
* Setting a label property will over ride it to a differrent value.
*/
label?: string;
attr: string;
obj: any; /** Really, it's OK here! See safeStringFetch */
}
function MetaInfo({ obj, attr, label }: MetaInfoProps) {
let top = label || _.startCase(attr.split("_").join());
let bottom = safeStringFetch(obj, attr);
return <div className="coordinate">
<label>{top}</label>
<span>{bottom || "unknown"}</span>
</div>;
}

View File

@ -4,11 +4,10 @@ import { JSXChildren } from "../util";
interface WidgetFooterProps {
children?: JSXChildren;
helpText?: string;
title: string;
}
export function WidgetFooter(props: WidgetFooterProps) {
return <div className="widget-footer">
{props.children}
</div>;
}