WIP, browserify

pull/218/head
Rick Carlino 2015-09-22 12:17:06 -05:00
parent 0201975c5a
commit 2edee7d820
18 changed files with 23068 additions and 97 deletions

View File

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ spec/reports
test/tmp
test/version_tmp
tmp
npm-debug.log
*.log
log/*.log
# YARD artifacts

View File

@ -3,8 +3,10 @@ Style/StringLiterals:
InlineComment:
Enabled: false
java_script:
enabled: true
enabled: false
config_file: config/jshint.json
ruby:
enabled: true
enabled: false
config_file: config/rubocup.yml
scss:
enabled: false

View File

@ -1 +1 @@
2.2.0
2.2.3

View File

@ -1,3 +1,4 @@
<div class="farm-designer" id="farm-designer-app">
</div>
<%= javascript_include_tag "farmbot_app/react/farm_designer" %>
<%= javascript_include_tag "/js/development.js" %>

View File

@ -1,71 +0,0 @@
=Navigating=
visit('/projects')
visit(post_comments_path(post))
=Clicking links and buttons=
click_link('id-of-link')
click_link('Link Text')
click_button('Save')
click('Link Text') # Click either a link or a button
click('Button Value')
=Interacting with forms=
fill_in('First Name', :with => 'John')
fill_in('Password', :with => 'Seekrit')
fill_in('Description', :with => 'Really Long Text…')
choose('A Radio Button')
check('A Checkbox')
uncheck('A Checkbox')
attach_file('Image', '/path/to/image.jpg')
select('Option', :from => 'Select Box')
=scoping=
within("//li[@id='employee']") do
fill_in 'Name', :with => 'Jimmy'
end
within(:css, "li#employee") do
fill_in 'Name', :with => 'Jimmy'
end
within_fieldset('Employee') do
fill_in 'Name', :with => 'Jimmy'
end
within_table('Employee') do
fill_in 'Name', :with => 'Jimmy'
end
=Querying=
page.has_xpath?('//table/tr')
page.has_css?('table tr.foo')
page.has_content?('foo')
page.should have_xpath('//table/tr')
page.should have_css('table tr.foo')
page.should have_content('foo')
page.should have_no_content('foo')
find_field('First Name').value
find_link('Hello').visible?
find_button('Send').click
find('//table/tr').click
locate("//*[@id='overlay'").find("//h1").click
all('a').each { |a| a[:href] }
=Scripting=
result = page.evaluate_script('4 + 4');
=Debugging=
save_and_open_page
=Asynchronous JavaScript=
click_link('foo')
click_link('bar')
page.should have_content('baz')
page.should_not have_xpath('//a')
page.should have_no_xpath('//a')
=XPath and CSS=
within(:css, 'ul li') { ... }
find(:css, 'ul li').text
locate(:css, 'input#name').value
Capybara.default_selector = :css
within('ul li') { ... }
find('ul li').text
locate('input#name').value

28
gulpfile.js 100644
View File

@ -0,0 +1,28 @@
var gulp = require('gulp'),
babel = require('gulp-babel'),
rollup = require('gulp-rollup'),
concat = require('gulp-concat'),
browserify = require('browserify'),
source = require('vinyl-source-stream');
var paths = {
jsIn: './javascripts/**/**/*.jsx',
jsOut: './public/js'
};
gulp.task('default', function () {
var input = paths.jsIn;
var output = gulp.dest(paths.jsOut);
// return gulp.src(input)
// .pipe(babel({ }))
// .pipe(concat("development.js"))
// .pipe(output);
});
gulp.task("hmm", function() {
var output = gulp.dest(paths.jsOut + '/whatever.js');
browserify({entries: ['javascripts/farm_designer.jsx'], extensions: ['.jsx']})
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest('public/development.js'));
})

View File

@ -0,0 +1,107 @@
//= require farmbot_app/react/init
//= require farmbot_app/react/menus/crop_inventory
//= require farmbot_app/react/menus/plant_catalog
//= require farmbot_app/react/menus/crop_info
//= require farmbot_app/react/menus/calendar
//= require farmbot_app/react/menus/schedule_creation
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
class Crop {
constructor(options) {
this.name = (options.name || "Untitled Crop");
this.age = (options.age || _.random(0, 5));
this._id = (options._id || _.random(0, 1000));
this.imgUrl = (options.imgUrl || "/designer_icons/unknown.svg");
}
}
fakeCrops = [
new Crop({name: "Blueberry", imgUrl: "/designer_icons/blueberry.svg"}),
new Crop({name: "Cabbage", imgUrl: "/designer_icons/cabbage.svg"}),
new Crop({name: "Pepper", imgUrl: "/designer_icons/pepper.svg"}),
new Crop({name: "Cilantro", imgUrl: "/designer_icons/cilantro.svg"}),
];
Fb.ToolTip = React.createClass({
render: function(){
return(
<div>
<div className="fb-tooltip">
<div className="tooltip-text">
{ this.props.desc }
</div>
</div>
<span className={ (this.props.color || "") + " plus-circle" }
onClick={ this.props.action }>
</span>
</div>
);
}
});
Fb.DesignerApp = class extends React.Component {
render() {
return <div className="farm-designer-body">
<p>{Fb.store.getState().UI.inventoryTab}</p>
<div className="farm-designer-left">
<div id="designer-left">
{ React.createElement(Fb.store.getState().UI.leftMenu) }
</div>
</div>
<div className="farm-designer-middle">
<div></div>
</div>
<div className="farm-designer-right">
<div id="designer-right">
<Fb.Calendar />
</div>
</div>
</div>
}
}
Fb.wow = connect(s => s)(Fb.DesignerApp);
Fb.initialState = {
UI: {
leftMenu: Fb.Inventory.Content, // Left side of screen
inventoryTab: 'Zones' // Current tab selection in "Inventory"
}
};
Fb.reducer = function(state, action) {
if (action.type === "@@redux/INIT") {
return state;
} else {
return(Fb.reducer[action.type] || Fb.reducer.UNKNOWN)(state, action.params);
};
};
Fb.reducer.UNKNOWN = function(state, params) {
console.warn("Unknown dispatcher");
return state;
};
Fb.reducer.CLICK_INVENTORY_TAB = function(state, params) {
return _.merge({}, state, {UI: {inventoryTab: params}});
};
Fb.store = createStore(Fb.reducer, Fb.initialState);
$(document).ready(function() {
var dom = document.getElementById("farm-designer-app");
var menu = (
<Provider store={Fb.store}>
{()=><Fb.wow/>}
</Provider>);
connect()(menu);
if (dom){ React.render(menu, dom);
} else{
console.info('Not loading designer.');
};
});

View File

@ -0,0 +1,2 @@
Fb = (window.Fb || {});

View File

@ -0,0 +1,102 @@
Fb.CalendarMenu = class extends React.Component {
render() {
return <div className="search-box-wrapper purple-content">
<input className="search" placeholder="Search"/>
</div>;
}
};
Fb.CalendarContent = class extends React.Component {
render() {
var events = _(Fb.scheduledEvents)
.sortBy('time')
.map((s, k) => <Fb.ScheduleEventView scheduledEvent={s} key={k}/>)
.value();
return (
<div className="calendar">
<div className="widget-wrapper">
<div className="row">
<div className="small-12 columns">
<div className="header-wrapper">
<h5>Calendar</h5>
</div>
</div>
</div>
<div className="row">
<div className="small-12 columns">
<div className="content-wrapper calendar-wrapper">
<div className="row date-flipper">
<div className="small-2 columns">
<i className="fa fa-arrow-left arrow-button radius"></i>
</div>
<div className="small-8 columns">
<h6 className="date">Feb 28</h6>
</div>
<div className="small-2 columns">
<i className="fa fa-arrow-right arrow-button radius right"></i>
</div>
</div>
{ events }
</div>
</div>
</div>
</div>
<Fb.ToolTip action={ Fb.renderScheduleCreation } desc="Schedule new event" color="dark-purple"/>
</div>);
}
};
Fb.ScheduledEvent = class {
constructor (options) {
this.time = (options.time || new Date());
this.desc = (options.desc || "Untitled Event");
this.icon = (options.icon || "fi-trees");
}
formatTime() {
var hours = this.time.getHours();
return `${hours} ${(hours > 12) ? "AM" : "PM"}`;
}
hasPassed () { return this.time < new Date(); }
};
Fb.scheduledEvents = [
(new Fb.ScheduledEvent({desc: "Photograph",
time: new Date("02-28-2015 06:00")})),
(new Fb.ScheduledEvent({desc: "Weed Crops",
time: new Date("02-28-2015 07:00")})),
(new Fb.ScheduledEvent({desc: "Spectral Rdg",
time: new Date("02-28-2015 09:00")}))
]
Fb.ScheduleEventView = class extends React.Component {
render () {
var evnt = this.props.scheduledEvent;
return <div className="row event { this.hasPassed() ? 'past' : '' }">
<div className="small-12 columns">
<div className="event-time">
{ evnt.formatTime() }
</div>
<i className="event-icon fi-camera"></i>
<div className="event-title">{ evnt.desc }</div>
<i className="edit-icon fi-pencil right"></i>
</div>
</div>;
}
}
Fb.Calendar = class extends React.Component {
render () {
return <div>
<Fb.CalendarMenu />
<Fb.CalendarContent />
</div>
}
}
Fb.renderCalendar = function() {
React.render(<Fb.Calendar />, Fb.rightMenu);
};

View File

@ -0,0 +1,117 @@
class MapPoint {
constructor(x, y) {
this.x = x || 0;
this.y = y || 0;
}
}
Fb.MapPointView = class extends React.Component {
render() {
var style = {
position: 'absolute',
left: (this.props.point.x - 20),
top: (this.props.point.y - 40)
};
return <img style={style} src="/designer_icons/pin.png"></img>;
}
};
Fb.CropInfoContent = class extends React.Component {
move() { Fb.renderInventory() }
drop (e) {
var data = this.state.data.concat(new MapPoint(e.clientX, e.clientY));
this.setState({data: data});
}
constructor() {
super();
this.render = this.render.bind(this);
this.state = {data: []};
}
get points() {
var points = this.state.data.map(
(p, k) => <Fb.MapPointView point={ p } key={k} />
);
return points;
}
render() {
return <div>
<div className="green-content">
<div className="search-box-wrapper">
<p>
<a href="#" onClick={ this.move }>
<i className="fa fa-arrow-left"></i>
</a>
{ this.props.crop.name }
</p>
</div>
</div>
<div className="designer-info">
<div className="crop-drag-info-tile">
<h6>Crop Image</h6>
<img className="crop-drag-info-image"
src={this.props.crop.imgUrl}
onDragEnd={ this.drop.bind(this) }/>
<div className="crop-info-overlay">
To plant, drag and drop into map
</div>
</div>
<div>
<h6>
Crop Info
<span><a href="#">Edit</a></span>
</h6>
<ul>
<li> Expected height: 28 inches </li>
<li> Expected diameter: 44 inches </li>
<li> Life Expectancy: 8 years </li>
</ul>
</div>
<div>
<h6>
Planting Tips
<span><a href="#">Edit</a></span>
</h6>
<ul>
<li> Plant in full sun </li>
<li> Fruits most in acidic soil </li>
<li> Plant near melons </li>
</ul>
</div>
<div>
<h6>
Default Regimens
<span><a href="#">Edit</a></span>
</h6>
<ul>
<li> Blueberries by OpenFarm</li>
<li> Soil Acidifier </li>
</ul>
</div>
<div>
<h6>
Delete This Crop
</h6>
<p>
Note: You will no longer be able to plant this crop.
</p>
<span>
<button className="red">
Delete
</button>
</span>
<div id="drop-area">
{ this.points }
</div>
</div>
</div>
</div>
}
}
Fb.renderCropInfo = function(crop) {
React.render(<Fb.CropInfoContent crop={crop} />, Fb.leftMenu);
};

View File

@ -0,0 +1,164 @@
Fb.Inventory = {}
Fb.Inventory.Tab = class extends React.Component {
render() {
return <li onClick={ this.handleClick.bind(this) }>
<a href="#"
wow={this.store}
className={this.props.active ? "active" : ""}>
{ this.props.name }
</a>
</li>
}
handleClick() {
debugger;
Fb.store.dispatch({type: "CLICK_INVENTORY_TAB", params: this.props.name})
}
}
Fb.Inventory.Plants = class extends React.Component {
render() {
return(
<div>
<Fb.Inventory.List crops={ fakeCrops } />
<Fb.ToolTip action={ Fb.renderCatalog } desc="Add a new plant" color="dark-green"/>
</div>
);
}
};
Fb.Inventory.Groups = class extends React.Component {
render() {
return(
<div className="designer-info">
<h6>My Groups</h6>
<ul>
<li>
<a href="#">Lucky Cabages</a>
<p>5 Plants</p>
</li>
<li>
<a href="#">Lucky Cabages</a>
<p>5 Plants</p>
</li>
</ul>
<h6>Zone Auto-Groups</h6>
<ul>
<li>
<a href="#">Plants in "Broccoli Overlord"</a>
<p>10 Plants</p>
</li>
<li>
<a href="#">Plants in "Flower Patch"</a>
<p>7 Plants</p>
</li>
</ul>
<h6>Crop Auto-Groups</h6>
<ul>
<li>
<a href="#">All Strawberries</a>
<p>1 plant</p>
</li>
<li>
<a href="#">All Flowers</a>
<p>42 plants</p>
</li>
</ul>
<Fb.ToolTip action={ Fb.renderCatalog }
desc="Add a new group"
color="dark-green"/>
</div>
)
}
};
Fb.Inventory.Zones = class extends React.Component {
render() {
return(
<div className="designer-info">
<h6>My Zones</h6>
<ul>
<li>
<a href="#">Front area</a>
<p>18 Square Feet</p>
</li>
<li>
<a href="#">Needs Compost</a>
<p>5 Square Feet</p>
</li>
</ul>
<h6>Auto-Zones</h6>
<ul>
<li>
<a href="#">Broccoli Overlord</a>
<p>60 Square Feet</p>
</li>
</ul>
<Fb.ToolTip action={ Fb.renderCatalog }
desc="Add New Zone"
color="dark-green"/>
</div>
)
}
};
Fb.Inventory.Item = class extends React.Component {
render() {
return(
<li>
<a href="#"> {this.props.crop.name} </a>
<div>{this.props.crop.age} days old</div>
</li>);
}
};
Fb.Inventory.List = class extends React.Component {
render() {
var crops = this.props.crops.map(
(crop, k) => <Fb.Inventory.Item crop={crop} key={ k } />
);
return(<ul className="crop-inventory"> { crops } </ul>);
}
};
Fb.Inventory.Content = class extends React.Component {
get tabName() {
return (Fb.store.getState().UI.inventoryTab || "Plants")
}
currentTab() { return Fb.Inventory[this.tabName] }
isActive(item) { return this.tabName === item }
render() {
return (
<div>
<div className="green-content">
<div className="search-box-wrapper">
<input className="search" placeholder="Search"/>
</div>
<ul className="tabs">
{
["Plants", "Groups", "Zones"].map(function(item, i) {
return <Fb.Inventory.Tab key={i}
name={item}
active={this.isActive(item)}/>;
}.bind(this))}
</ul>
</div>
{
React.createElement(this.currentTab())
}
</div>
)
}
};
Fb.renderInventory = function(){
React.render(<Fb.Inventory.Content />, Fb.leftMenu);
};

View File

@ -0,0 +1,51 @@
Fb.PlantCatalogTile = class extends React.Component {
render() {
return(
<div className="plantCatalogTile" onClick={ e => { Fb.renderCropInfo(this.props.crop); } }>
<div className="row">
<div className="small-12 columns">
<div className="small-header-wrapper">
<h5>{ this.props.crop.name }</h5>
</div>
</div>
</div>
<div className="row">
<div className="small-12 columns">
<div className="content-wrapper">
<p> <img src={this.props.crop.imgUrl} /> </p>
</div>
</div>
</div>
</div>
);
}
};
Fb.PlantCatalog = class extends React.Component {
render() {
var crops = fakeCrops.map(
(crop, k) => <Fb.PlantCatalogTile crop={crop} key={ k } />
);
return <div id="designer-left">
<div className="green-content">
<div className="search-box-wrapper">
<p>
<a href="#" onClick={ "" }>
<i className="fa fa-arrow-left"></i>
</a>
Choose a Crop
</p>
</div>
</div>
<div crops={ fakeCrops }>
<br/>
{ crops }
</div>
</div>
}
}
Fb.renderCatalog = function() {
alert('this is where you left off. Add a redux dispatcher here.');
};

View File

@ -0,0 +1,70 @@
Fb.ScheduleCreation = class extends React.Component {
back() { Fb.renderCalendar(); }
render() {
var html = (
<div>
<div>
<div className="search-box-wrapper purple-content">
<p>
<a href="#" onClick={ this.back }>
<i className="fa fa-arrow-left"></i>
</a>
Schedule Event
</p>
</div>
</div>
<div className="designer-info">
<h6>Chose a Sequence or Regimen</h6>
<select>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
<h6>Starts</h6>
<div className="flex">
<input placeholder="Today"
type="text"
className="flex3"></input>
<select className="flex3">
<option value="volvo">12:30</option>
<option value="saab">12:00</option>
</select>
</div>
<h6>Repeats</h6>
<div className="flex">
<input placeholder="2"
type="text"
className="flex3"></input>
<select className="flex3">
<option value="volvo">days</option>
<option value="saab">hours</option>
</select>
<input type="checkbox" name="wow" value="no">Does not repeat</input>
</div>
<h6>Ends</h6>
<div className="flex">
<input placeholder="Today"
type="text"
className="flex3"></input>
<select className="flex3">
<option value="volvo">12:30</option>
<option value="saab">12:00</option>
</select>
</div>
<div>
<button className="purple-content">
Save
</button>
</div>
</div>
</div>
)
return html;
}
}
Fb.renderScheduleCreation = function() {
React.render(<Fb.ScheduleCreation />, Fb.rightMenu);
};

View File

@ -1,22 +0,0 @@
0 info it worked if it ends with ok
1 verbose cli [ '/usr/bin/node', '/usr/bin/npm', 'install' ]
2 info using npm@1.4.28
3 info using node@v0.10.40
4 verbose node symlink /usr/bin/node
5 error install Couldn't read dependencies
6 error Failed to parse json
6 error Unexpected token /
7 error File: /home/rick/code/farmbot-web-app/package.json
8 error Failed to parse package.json data.
8 error package.json must be actual JSON, not just JavaScript.
8 error
8 error This is not a bug in npm.
8 error Tell the package author to fix their package.json file. JSON.parse
9 error System Linux 3.13.0-37-generic
10 error command "/usr/bin/node" "/usr/bin/npm" "install"
11 error cwd /home/rick/code/farmbot-web-app
12 error node -v v0.10.40
13 error npm -v 1.4.28
14 error file /home/rick/code/farmbot-web-app/package.json
15 error code EJSONPARSE
16 verbose exit [ 1, true ]

View File

@ -16,6 +16,11 @@
"url": "https://github.com/rickcarlino/farmbot-web-app/issues"
},
"homepage": "https://github.com/rickcarlino/farmbot-web-app",
"browserify": {
"transform": [
"babelify"
]
},
"dependencies": {
"angular": "^1.3.19",
"angular-ui-sortable": "^0.13.4",
@ -25,5 +30,17 @@
"react-redux": "^2.1.2",
"reactify": "^1.1.1",
"redux": "^3.0.0"
},
"devDependencies": {
"babelify": "^6.3.0",
"browserify": "^11.1.0",
"gulp": "^3.9.0",
"gulp-babel": "^5.2.1",
"gulp-concat": "^2.6.0",
"gulp-rollup": "^1.0.1",
"gulp-util": "^3.0.6",
"vinyl-source-stream": "^1.1.0",
"webpack": "^1.12.2",
"webpack-stream": "^2.1.0"
}
}

File diff suppressed because it is too large Load Diff

1349
public/js/all.js 100644

File diff suppressed because it is too large Load Diff