Alex Mikhalev
7 years ago
25 changed files with 1063 additions and 200 deletions
@ -1,57 +0,0 @@
@@ -1,57 +0,0 @@
|
||||
import * as classNames from "classnames"; |
||||
import * as React from "react"; |
||||
import { Input, InputProps } from "semantic-ui-react"; |
||||
|
||||
import { Duration } from "@common/Duration"; |
||||
|
||||
export default class DurationInput extends React.Component<{ |
||||
duration: Duration, |
||||
onDurationChange: (newDuration: Duration) => void, |
||||
className?: string, |
||||
}> { |
||||
render() { |
||||
const duration = this.props.duration; |
||||
const className = classNames("field", "durationInput", this.props.className); |
||||
// const editing = this.props.onDurationChange != null;
|
||||
return ( |
||||
<div className={className}> |
||||
<label>Duration</label> |
||||
<div className="ui two fields"> |
||||
<Input |
||||
type="number" |
||||
className="field durationInput--minutes" |
||||
value={duration.minutes} |
||||
onChange={this.onMinutesChange} |
||||
label="M" |
||||
labelPosition="right" |
||||
/> |
||||
<Input |
||||
type="number" |
||||
className="field durationInput--seconds" |
||||
value={duration.seconds} |
||||
onChange={this.onSecondsChange} |
||||
max="60" |
||||
label="S" |
||||
labelPosition="right" |
||||
/> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
private onMinutesChange: InputProps["onChange"] = (e, { value }) => { |
||||
if (isNaN(Number(value))) { |
||||
return; |
||||
} |
||||
const newMinutes = parseInt(value, 10); |
||||
this.props.onDurationChange(this.props.duration.withMinutes(newMinutes)); |
||||
} |
||||
|
||||
private onSecondsChange: InputProps["onChange"] = (e, { value }) => { |
||||
if (isNaN(Number(value))) { |
||||
return; |
||||
} |
||||
const newSeconds = parseInt(value, 10); |
||||
this.props.onDurationChange(this.props.duration.withSeconds(newSeconds)); |
||||
} |
||||
} |
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
import * as classNames from "classnames"; |
||||
import * as React from "react"; |
||||
import { Form, Input, InputProps } from "semantic-ui-react"; |
||||
|
||||
import { Duration } from "@common/Duration"; |
||||
|
||||
export default class DurationView extends React.Component<{ |
||||
label?: string, |
||||
inline?: boolean, |
||||
duration: Duration, |
||||
onDurationChange?: (newDuration: Duration) => void, |
||||
className?: string, |
||||
}> { |
||||
render() { |
||||
const { duration, label, inline, onDurationChange } = this.props; |
||||
const className = classNames("durationInput", this.props.className); |
||||
if (onDurationChange) { |
||||
return ( |
||||
<React.Fragment> |
||||
<Form.Field inline={inline}> |
||||
{label && <label>{label}</label>} |
||||
<div className="durationInputs"> |
||||
<Input |
||||
type="number" |
||||
className="durationInput minutes" |
||||
value={duration.minutes} |
||||
onChange={this.onMinutesChange} |
||||
label="M" |
||||
labelPosition="right" |
||||
onWheel={this.onWheel} |
||||
/> |
||||
<Input |
||||
type="number" |
||||
className="durationInput seconds" |
||||
value={duration.seconds} |
||||
onChange={this.onSecondsChange} |
||||
max="60" |
||||
label="S" |
||||
labelPosition="right" |
||||
onWheel={this.onWheel} |
||||
/> |
||||
</div> |
||||
</Form.Field> |
||||
</React.Fragment> |
||||
); |
||||
} else { |
||||
return ( |
||||
<span className={className}> |
||||
{label && <label>{label}</label>} {duration.minutes}M {duration.seconds}S |
||||
</span> |
||||
); |
||||
} |
||||
} |
||||
|
||||
private onMinutesChange: InputProps["onChange"] = (e, { value }) => { |
||||
if (!this.props.onDurationChange || isNaN(Number(value))) { |
||||
return; |
||||
} |
||||
const newMinutes = Number(value); |
||||
this.props.onDurationChange(this.props.duration.withMinutes(newMinutes)); |
||||
} |
||||
|
||||
private onSecondsChange: InputProps["onChange"] = (e, { value }) => { |
||||
if (!this.props.onDurationChange || isNaN(Number(value))) { |
||||
return; |
||||
} |
||||
const newSeconds = Number(value); |
||||
this.props.onDurationChange(this.props.duration.withSeconds(newSeconds)); |
||||
} |
||||
|
||||
private onWheel = () => { |
||||
// do nothing
|
||||
} |
||||
} |
@ -1,19 +1,94 @@
@@ -1,19 +1,94 @@
|
||||
import classNames = require("classnames"); |
||||
import { observer } from "mobx-react"; |
||||
import * as React from "react"; |
||||
import { Form, List } from "semantic-ui-react"; |
||||
|
||||
import { DurationView, SectionChooser } from "@app/components/index"; |
||||
import { Duration } from "@common/Duration"; |
||||
import { ProgramItem, Section} from "@common/sprinklersRpc"; |
||||
|
||||
export default function ProgramSequenceView({ sequence, sections }: { |
||||
sequence: ProgramItem[], sections: Section[], |
||||
}) { |
||||
const sequenceItems = sequence.map((item, index) => { |
||||
const section = sections[item.section]; |
||||
const duration = Duration.fromSeconds(item.duration); |
||||
import { ProgramItem, Section } from "@common/sprinklersRpc"; |
||||
|
||||
@observer |
||||
class ProgramSequenceItem extends React.Component<{ |
||||
sequenceItem: ProgramItem, sections: Section[], onChange?: (newItem: ProgramItem) => void, |
||||
}> { |
||||
renderContent() { |
||||
const { sequenceItem, sections } = this.props; |
||||
const editing = this.props.onChange != null; |
||||
const section = sections[sequenceItem.section]; |
||||
const duration = Duration.fromSeconds(sequenceItem.duration); |
||||
|
||||
if (editing) { |
||||
return ( |
||||
<Form.Group inline> |
||||
<SectionChooser |
||||
label="Section" |
||||
inline |
||||
sections={sections} |
||||
value={section} |
||||
onChange={this.onSectionChange} |
||||
/> |
||||
<DurationView |
||||
label="Duration" |
||||
inline |
||||
duration={duration} |
||||
onDurationChange={this.onDurationChange} |
||||
/> |
||||
</Form.Group> |
||||
); |
||||
} else { |
||||
return ( |
||||
<React.Fragment> |
||||
<List.Header>{section.toString()}</List.Header> |
||||
<List.Description>for {duration.toString()}</List.Description> |
||||
</React.Fragment> |
||||
); |
||||
} |
||||
} |
||||
|
||||
render() { |
||||
return ( |
||||
<li key={index}> |
||||
<em>"{section.name}"</em> for {duration.toString()} |
||||
</li> |
||||
<List.Item> |
||||
<List.Icon name="caret right"/> |
||||
<List.Content>{this.renderContent()}</List.Content> |
||||
</List.Item> |
||||
); |
||||
}); |
||||
return <ul>{sequenceItems}</ul>; |
||||
} |
||||
|
||||
private onSectionChange = (newSection: Section) => { |
||||
if (!this.props.onChange) { |
||||
return; |
||||
} |
||||
this.props.onChange(new ProgramItem({ |
||||
...this.props.sequenceItem, section: newSection.id, |
||||
})); |
||||
} |
||||
|
||||
private onDurationChange = (newDuration: Duration) => { |
||||
if (!this.props.onChange) { |
||||
return; |
||||
} |
||||
this.props.onChange(new ProgramItem({ |
||||
...this.props.sequenceItem, duration: newDuration.toSeconds(), |
||||
})); |
||||
} |
||||
} |
||||
|
||||
@observer |
||||
export default class ProgramSequenceView extends React.Component<{ |
||||
sequence: ProgramItem[], sections: Section[], editing?: boolean, |
||||
}> { |
||||
render() { |
||||
const { sequence, sections } = this.props; |
||||
const editing = this.props.editing || false; |
||||
const className = classNames("programSequence", { editing }); |
||||
const sequenceItems = sequence.map((item, index) => { |
||||
const onChange = editing ? (newItem: ProgramItem) => this.changeItem(newItem, index) : undefined; |
||||
return <ProgramSequenceItem sequenceItem={item} sections={sections} key={index} onChange={onChange}/>; |
||||
}); |
||||
return <List className={className}>{sequenceItems}</List>; |
||||
} |
||||
|
||||
private changeItem = (newItem: ProgramItem, index: number) => { |
||||
this.props.sequence[index] = newItem; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
import { computed } from "mobx"; |
||||
import { observer } from "mobx-react"; |
||||
import * as React from "react"; |
||||
import { DropdownItemProps, DropdownProps, Form } from "semantic-ui-react"; |
||||
|
||||
import { Section } from "@common/sprinklersRpc"; |
||||
|
||||
@observer |
||||
export default class SectionChooser extends React.Component<{ |
||||
label?: string, |
||||
inline?: boolean, |
||||
sections: Section[], |
||||
value?: Section, |
||||
onChange?: (section: Section) => void, |
||||
}> { |
||||
render() { |
||||
const { label, inline, sections, value, onChange } = this.props; |
||||
let section = (value == null) ? "" : sections.indexOf(value); |
||||
section = (section === -1) ? "" : section; |
||||
const onSectionChange = (onChange == null) ? undefined : this.onSectionChange; |
||||
if (onChange == null) { |
||||
return <React.Fragment>{label || ""} '{value ? value.toString() : ""}'</React.Fragment>; |
||||
} |
||||
return ( |
||||
<Form.Select |
||||
label={label} |
||||
inline={inline} |
||||
placeholder="Section" |
||||
options={this.sectionOptions} |
||||
value={section} |
||||
onChange={onSectionChange} |
||||
/> |
||||
); |
||||
} |
||||
|
||||
private onSectionChange = (e: React.SyntheticEvent<HTMLElement>, v: DropdownProps) => { |
||||
this.props.onChange!(this.props.sections[v.value as number]); |
||||
} |
||||
|
||||
@computed |
||||
private get sectionOptions(): DropdownItemProps[] { |
||||
return this.props.sections.map((s, i) => ({ |
||||
text: s ? `${s.id}: ${s.name}` : null, |
||||
value: i, |
||||
})); |
||||
} |
||||
} |
After Width: | Height: | Size: 955 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
Loading…
Reference in new issue