More improvements
This commit is contained in:
parent
d895e2e3e9
commit
8c94246f33
@ -1,22 +1,25 @@
|
||||
import "app/style/app.css";
|
||||
import "font-awesome/css/font-awesome.css";
|
||||
import {observer} from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import DevTools from "mobx-react-devtools";
|
||||
import * as React from "react";
|
||||
|
||||
import { Item } from "semantic-ui-react";
|
||||
import { DeviceView, MessagesView } from ".";
|
||||
import { SprinklersDevice } from "../sprinklers";
|
||||
import { UiStore } from "../ui";
|
||||
|
||||
import "app/style/app.css";
|
||||
import "font-awesome/css/font-awesome.css";
|
||||
import "semantic-ui-css/semantic.css";
|
||||
import {Item} from "semantic-ui-react";
|
||||
import {DeviceView, MessagesView} from ".";
|
||||
import {SprinklersDevice} from "../sprinklers";
|
||||
import {UiStore} from "../ui";
|
||||
|
||||
@observer
|
||||
export default class App extends React.PureComponent<{ device: SprinklersDevice, uiStore: UiStore }, any> {
|
||||
export default class App extends React.Component<{ device: SprinklersDevice, uiStore: UiStore }> {
|
||||
render() {
|
||||
return <Item.Group divided>
|
||||
<MessagesView uiStore={this.props.uiStore}/>
|
||||
<DeviceView device={this.props.device}/>
|
||||
<DevTools/>
|
||||
</Item.Group>;
|
||||
return (
|
||||
<Item.Group divided>
|
||||
<MessagesView uiStore={this.props.uiStore} />
|
||||
<DeviceView device={this.props.device} />
|
||||
<DevTools />
|
||||
</Item.Group>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,19 +7,22 @@ import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from ".
|
||||
import { SprinklersDevice } from "../sprinklers";
|
||||
import FontAwesome = require("react-fontawesome");
|
||||
|
||||
const ConnectionState = ({ connected }: { connected: boolean }) =>
|
||||
<span className={classNames({
|
||||
const ConnectionState = ({ connected }: { connected: boolean }) => {
|
||||
const classes = classNames({
|
||||
"device--connectionState": true,
|
||||
"device--connectionState-connected": connected,
|
||||
"device--connectionState-disconnected": !connected,
|
||||
})}>
|
||||
<FontAwesome name={connected ? "plug" : "chain-broken"} />
|
||||
|
||||
{connected ? "Connected" : "Disconnected"}
|
||||
</span>;
|
||||
});
|
||||
return (
|
||||
<span className={classes}>
|
||||
<FontAwesome name={connected ? "plug" : "chain-broken"} />
|
||||
{connected ? "Connected" : "Disconnected"}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
@observer
|
||||
export default class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, {}> {
|
||||
export default class DeviceView extends React.Component<{ device: SprinklersDevice }> {
|
||||
render() {
|
||||
const { id, connected, sections, programs, sectionRunner } = this.props.device;
|
||||
return (
|
||||
@ -31,7 +34,7 @@ export default class DeviceView extends React.PureComponent<{ device: Sprinklers
|
||||
<ConnectionState connected={connected} />
|
||||
</Header>
|
||||
<Item.Meta>
|
||||
|
||||
Raspberry Pi Grinklers Instance
|
||||
</Item.Meta>
|
||||
<SectionRunnerView sectionRunner={sectionRunner} />
|
||||
<SectionTable sections={sections} />
|
||||
|
@ -1,30 +1,47 @@
|
||||
import {observer} from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import {CSSTransitionGroup} from "react-transition-group";
|
||||
import {Message} from "semantic-ui-react";
|
||||
import {Message as UiMessage, UiStore} from "../ui";
|
||||
import { Message, MessageList, TransitionGroup } from "semantic-ui-react";
|
||||
import { Message as UiMessage, UiStore } from "../ui";
|
||||
|
||||
class MessageView extends React.Component<{
|
||||
uiStore: UiStore,
|
||||
message: UiMessage,
|
||||
index: number,
|
||||
}> {
|
||||
|
||||
@observer
|
||||
export default class MessagesView extends React.PureComponent<{ uiStore: UiStore }, {}> {
|
||||
render() {
|
||||
return <div className="messages">
|
||||
<CSSTransitionGroup transitionName="message" transitionAppear={true} transitionAppearTimeout={500}
|
||||
transitionEnterTimeout={500} transitionLeaveTimeout={500}>
|
||||
{this.props.uiStore.messages.map(this.renderMessage)}
|
||||
</CSSTransitionGroup>
|
||||
</div>;
|
||||
const { id, header, content, type } = this.props.message;
|
||||
return (
|
||||
<Message
|
||||
header={header}
|
||||
content={content}
|
||||
success={type === UiMessage.Type.Success}
|
||||
info={type === UiMessage.Type.Info}
|
||||
warning={type === UiMessage.Type.Warning}
|
||||
error={type === UiMessage.Type.Error}
|
||||
onDismiss={this.dismiss}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private renderMessage = (message: UiMessage, index: number) => {
|
||||
const {header, content, type} = message;
|
||||
return <Message key={message.id} className="message"
|
||||
header={header} content={content}
|
||||
success={type === UiMessage.Type.Success}
|
||||
info={type === UiMessage.Type.Info} warning={type === UiMessage.Type.Warning}
|
||||
error={type === UiMessage.Type.Error} onDismiss={() => this.dismiss(index)}/>;
|
||||
}
|
||||
|
||||
private dismiss(index: number) {
|
||||
this.props.uiStore.messages.splice(index, 1);
|
||||
private dismiss = () => {
|
||||
const { uiStore, index } = this.props;
|
||||
uiStore.messages.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class MessagesView extends React.Component<{ uiStore: UiStore }> {
|
||||
render() {
|
||||
const messages = this.props.uiStore.messages.map((message, index) => (
|
||||
<MessageView key={message.id} uiStore={this.props.uiStore} message={message} index={index} />
|
||||
));
|
||||
return (
|
||||
<div className="messages" >
|
||||
<TransitionGroup animation="scale" duration={200}>
|
||||
{messages}
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {observer} from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import {Table} from "semantic-ui-react";
|
||||
import {Program, Schedule} from "../sprinklers";
|
||||
import { Table } from "semantic-ui-react";
|
||||
import { Program, Schedule } from "../sprinklers";
|
||||
|
||||
@observer
|
||||
export class ScheduleView extends React.PureComponent<{ schedule: Schedule }, {}> {
|
||||
export class ScheduleView extends React.Component<{ schedule: Schedule }> {
|
||||
render() {
|
||||
return (
|
||||
<div>{JSON.stringify(this.props.schedule)}</div>
|
||||
@ -13,35 +13,39 @@ export class ScheduleView extends React.PureComponent<{ schedule: Schedule }, {}
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class ProgramTable extends React.PureComponent<{ programs: Program[] }, {}> {
|
||||
export default class ProgramTable extends React.Component<{ programs: Program[] }> {
|
||||
private static renderRows(program: Program, i: number): JSX.Element[] | null {
|
||||
if (!program) {
|
||||
return null;
|
||||
}
|
||||
const {name, running, enabled, schedule, sequence} = program;
|
||||
return [
|
||||
const { name, running, enabled, schedule, sequence } = program;
|
||||
const sequenceItems = sequence.map((item, index) => (
|
||||
<li key={index}>Section {item.section + 1 + ""} for
|
||||
{item.duration.minutes}M {item.duration.seconds}S</li>
|
||||
));
|
||||
return [(
|
||||
<Table.Row key={i}>
|
||||
<Table.Cell className="program--number">{"" + (i + 1)}</Table.Cell>
|
||||
<Table.Cell className="program--name">{name}</Table.Cell>
|
||||
<Table.Cell className="program--running">{running ? "Running" : "Not running"}</Table.Cell>
|
||||
<Table.Cell className="program--enabled">{enabled ? "Enabled" : "Not enabled"}</Table.Cell>
|
||||
</Table.Row>
|
||||
,
|
||||
), (
|
||||
<Table.Row key={i + .5}>
|
||||
<Table.Cell className="program--sequence" colSpan="4">
|
||||
<ul>
|
||||
{sequence.map((item) =>
|
||||
(<li>Section {item.section + 1 + ""} for
|
||||
{item.duration.minutes}M {item.duration.seconds}S</li>))}
|
||||
{sequenceItems}
|
||||
</ul>
|
||||
<ScheduleView schedule={schedule}/>
|
||||
<ScheduleView schedule={schedule} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
,
|
||||
];
|
||||
)];
|
||||
}
|
||||
|
||||
render() {
|
||||
const programRows = Array.prototype.concat.apply([],
|
||||
this.props.programs.map(ProgramTable.renderRows));
|
||||
|
||||
return (
|
||||
<Table celled>
|
||||
<Table.Header>
|
||||
@ -56,9 +60,7 @@ export default class ProgramTable extends React.PureComponent<{ programs: Progra
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{
|
||||
Array.prototype.concat.apply([], this.props.programs.map(ProgramTable.renderRows))
|
||||
}
|
||||
{programRows}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
|
@ -1,38 +1,41 @@
|
||||
import * as classNames from "classnames";
|
||||
import {observer} from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import {Table} from "semantic-ui-react";
|
||||
import { Table } from "semantic-ui-react";
|
||||
|
||||
import {Section} from "../sprinklers";
|
||||
import { Section } from "../sprinklers";
|
||||
import FontAwesome = require("react-fontawesome");
|
||||
|
||||
/* tslint:disable:object-literal-sort-keys */
|
||||
|
||||
@observer
|
||||
export default class SectionTable extends React.PureComponent<{ sections: Section[] }, {}> {
|
||||
export default class SectionTable extends React.Component<{ sections: Section[] }> {
|
||||
private static renderRow(section: Section, index: number) {
|
||||
if (!section) {
|
||||
return null;
|
||||
}
|
||||
const {name, state} = section;
|
||||
const { name, state } = section;
|
||||
const sectionStateClass = classNames({
|
||||
"section--state": true,
|
||||
"section--state-true": state,
|
||||
"section--state-false": !state,
|
||||
});
|
||||
const sectionState = state ?
|
||||
(<span><FontAwesome name="tint" /> Irrigating</span>)
|
||||
: "Not irrigating";
|
||||
return (
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell className="section--number">{"" + (index + 1)}</Table.Cell>
|
||||
<Table.Cell className="section--name">{name}</Table.Cell>
|
||||
<Table.Cell className={classNames({
|
||||
"section--state": true,
|
||||
"section--state-true": state,
|
||||
"section--state-false": !state,
|
||||
})}>{state ?
|
||||
(<span><FontAwesome name="tint"/> Irrigating</span>)
|
||||
: "Not irrigating"}
|
||||
</Table.Cell>
|
||||
<Table.Cell className={sectionStateClass}>{sectionState}</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<Table celled striped>
|
||||
const rows = this.props.sections.map(SectionTable.renderRow);
|
||||
return (
|
||||
<Table celled striped>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="3">Sections</Table.HeaderCell>
|
||||
@ -44,9 +47,7 @@ export default class SectionTable extends React.PureComponent<{ sections: Sectio
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{
|
||||
this.props.sections.map(SectionTable.renderRow)
|
||||
}
|
||||
{rows}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
|
@ -14,15 +14,19 @@ uiStore.addMessage(new Message("asdf", "boo!", Message.Type.Error));
|
||||
|
||||
const rootElem = document.getElementById("app");
|
||||
|
||||
ReactDOM.render(<AppContainer>
|
||||
<App device={device} uiStore={uiStore} />
|
||||
</AppContainer>, rootElem);
|
||||
const doRender = (Component: typeof App) => {
|
||||
ReactDOM.render((
|
||||
<AppContainer>
|
||||
<Component device={device} uiStore={uiStore} />
|
||||
</AppContainer>
|
||||
), rootElem);
|
||||
};
|
||||
|
||||
doRender(App);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept("./components/App", () => {
|
||||
const NextApp = require<any>("./components/App").default as typeof App;
|
||||
ReactDOM.render(<AppContainer>
|
||||
<NextApp device={device} uiStore={uiStore} />
|
||||
</AppContainer>, rootElem);
|
||||
doRender(NextApp);
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {observable} from "mobx";
|
||||
import { getRandomId } from "./utils";
|
||||
|
||||
export class Message {
|
||||
id: string;
|
||||
@ -7,7 +8,7 @@ export class Message {
|
||||
type: Message.Type = Message.Type.Default;
|
||||
|
||||
constructor(header: string, content: string = "", type: Message.Type = Message.Type.Default) {
|
||||
this.id = "" + Math.floor(Math.random() * 1000000000);
|
||||
this.id = "" + getRandomId();
|
||||
this.header = header;
|
||||
this.content = content;
|
||||
this.type = type;
|
||||
|
@ -7,3 +7,7 @@ export function checkedIndexOf<T>(o: T | number, arr: T[], type: string = "objec
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
export function getRandomId() {
|
||||
return Math.floor(Math.random() * 1000000000);
|
||||
}
|
||||
|
@ -46,31 +46,4 @@
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.message-enter,
|
||||
.message-appear {
|
||||
opacity: 0.01;
|
||||
}
|
||||
|
||||
.message-enter.message-enter-active,
|
||||
.message-appear.message-appear-active {
|
||||
opacity: 1;
|
||||
transition: all 500ms ease-in;
|
||||
}
|
||||
|
||||
.message-leave {
|
||||
/*opacity: 1;*/
|
||||
transform-origin: top;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.message-leave.message-leave-active {
|
||||
/*opacity: 0.01;*/
|
||||
transform: scaleY(0.01);
|
||||
/*height: 0;*/
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
transition: all 500ms ease-in;
|
||||
}
|
@ -58,6 +58,7 @@
|
||||
"style-loader": "^0.18.1",
|
||||
"ts-loader": "^2.1.0",
|
||||
"tslint": "^5.4.2",
|
||||
"tslint-react": "^3.2.0",
|
||||
"typescript": "^2.3.4",
|
||||
"webpack": "^3.0.0",
|
||||
"webpack-dev-server": "^2.4.4"
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"defaultSeverity": "warning",
|
||||
"extends": [
|
||||
"tslint:latest"
|
||||
"tslint:latest", "tslint-react"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
@ -33,7 +33,8 @@
|
||||
"no-submodule-imports": false,
|
||||
"no-unused-variable": [
|
||||
true
|
||||
]
|
||||
],
|
||||
"jsx-boolean-value": [ true, "never" ]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
|
12
yarn.lock
12
yarn.lock
@ -4248,6 +4248,12 @@ tslib@^1.7.1:
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec"
|
||||
|
||||
tslint-react@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tslint-react/-/tslint-react-3.2.0.tgz#851fb505201c63d0343c51726e6364f7e9ad2e99"
|
||||
dependencies:
|
||||
tsutils "^2.8.0"
|
||||
|
||||
tslint@^5.4.2:
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552"
|
||||
@ -4263,6 +4269,12 @@ tslint@^5.4.2:
|
||||
tslib "^1.7.1"
|
||||
tsutils "^2.8.1"
|
||||
|
||||
tsutils@^2.8.0:
|
||||
version "2.8.2"
|
||||
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.2.tgz#2c1486ba431260845b0ac6f902afd9d708a8ea6a"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
tsutils@^2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.1.tgz#3771404e7ca9f0bedf5d919a47a4b1890a68efff"
|
||||
|
Loading…
x
Reference in New Issue
Block a user