Skip to content

@jlongster /a-increment.js
Last active

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
var dom = Bloop.dom;
var Box = Bloop.createClass({
getInitialState: function() {
return { number: 0 };
},
updateNumber: function() {
this.state.number++;
},
render: function() {
return dom.div(
dom.span(this.state.number),
dom.button({ onClick: this.updateNumber }, 'Increment')
);
}
});
var box = Box();
function render() {
Bloop.renderComponent(box, document.body);
requestAnimationFrame(render);
}
render();
var dom = Bloop.dom;
// components
var App = Bloop.createClass({
getInitialState: function() {
return { number: 0 };
},
updateNumber: function(value) {
this.state.number = value;
},
render: function() {
return dom.div(
dom.span(this.state.number),
Toolbar({
number: this.state.number,
onChange: this.updateNumber
})
);
}
});
var Toolbar = Bloop.createClass({
render: function() {
return dom.div(
dom.button({
onClick: this.props.onChange.bind(null, this.props.number - 1)
}, 'decrement'),
dom.button({
onClick: this.props.onChange.bind(null, this.props.number + 1)
}, 'increment')
);
}
});
// render
var app = App();
function render() {
Bloop.renderComponent(app, document.body);
requestAnimationFrame(render);
}
render();
var dom = Bloop.dom;
var App = Bloop.createClass({
getInitialState: function() {
var items = [
{ text: 'Would I rather be feared or loved? Easy, both. ' +
'I want people to be afraid of how much they love me. ' +
'- Michael Scott' },
{ text: 'Sorry I annoyed you with my friendship. -Andy ' },
{ text: 'I am not a security threat. And, my middle name is ' +
'Kurt, not Fart. - Dwight Schrute' }
];
return { settings: { bigFonts: false,
color: null },
selectedItem: 'list',
items: items };
},
changeTab: function(tab) {
this.state.selectedItem = tab;
},
componentDidRender: function() {
window.parent.postMessage(
JSON.stringify({ from: 'shift',
state: this.state }),
window.location.protocol + '//' + window.location.host
);
},
changeSetting: function(name, value) {
this.state.settings[name] = value;
},
render: function() {
var s = this.state;
var content;
if(s.selectedItem === 'list') {
content = ItemList({ items: s.items });
}
else if(s.selectedItem === 'settings') {
content = Settings({ bigFonts: s.settings.bigFonts,
onChange: this.changeSetting
});
}
else if(s.selectedItem === 'about') {
content = dom.div(
{ className: 'about' },
'This is a sample demo'
);
}
return dom.div(
{ className: 'app ' + (s.settings.bigFonts ? 'big' : ''),
style: { backgroundColor: s.settings.color } },
Tabs({ selectedItem: s.selectedItem,
onChange: this.changeTab }),
content
);
}
});
var Tabs = Bloop.createClass({
render: function() {
var props = this.props;
function tab(id) {
return dom.a({
href: '#',
className: props.selectedItem === id ? 'selected' : '',
onClick: props.onChange.bind(null, id)
}, id);
}
return dom.div(
{ className: 'tabs' },
dom.ul(
dom.li(tab('list')),
dom.li(tab('settings')),
dom.li(tab('about'))
)
);
}
});
var Settings = Bloop.createClass({
randomizeColor: function() {
this.props.onChange(
'color',
'rgb(0,' + (Math.random() * 125 | 0) + ',0)'
);
},
reset: function() {
this.props.onChange('bigFonts', false);
this.props.onChange('color', null);
},
render: function() {
return dom.div(
{ className: 'settings' },
dom.div(
dom.input({ type: 'checkbox',
checked: this.props.bigFonts,
onClick: this.props.onChange.bind(
null,
'bigFonts',
!this.props.bigFonts
)
}),
'big fonts'
),
dom.div(dom.button({
onClick: this.randomizeColor
}, 'random color')),
dom.div(dom.button({ onClick: this.reset }, 'reset'))
);
}
});
var ItemList = Bloop.createClass({
render: function() {
return dom.ul(
{ className: 'itemlist' },
this.props.items.map(function(item) {
return dom.li(item.text);
})
);
}
});
// render
var app = App();
function render() {
Bloop.renderComponent(app, document.body);
requestAnimationFrame(render);
}
render();
// state update
window.addEventListener('message', function(msg) {
app.state = JSON.parse(msg.data);
});
var dom = Bloop.dom;
// components
var App = Bloop.createClass({
componentDidRender: function() {
var state = this.state;
// A hack because we don't have proper lifecycle methods
if(state.settingsOpen) {
var anchor = document.querySelector('.toolbar');
var rect = anchor.getBoundingClientRect();
var node = document.querySelector('.settings');
var nodeRect = node.getBoundingClientRect();
node.style.top = rect.bottom + 16 + 'px';
node.style.left = rect.right - nodeRect.width - 10 + 'px';
$('.settings input').focus();
}
else if(state.feed.items.length > 1) {
$('.feed input').focus();
}
},
handleMessage: function(e) {
if(e.keyCode === 13) {
this.submitMessage();
}
else {
this.state.newMessage = e.target.value;
}
},
submitMessage: function() {
var state = this.state;
if(state.newMessage) {
document.querySelector('.app input').value = '';
state.feed.items.unshift({
id: Math.random() * 10000 | 0,
author: state.username,
text: state.newMessage,
starred: false
});
state.newMessage = '';
}
},
openItem: function(id) {
var msg = getMessage(parseInt(id));
if(msg) {
this.state.selectedMessage = msg;
}
else {
this.state.selectedMessage = null;
}
},
updateSettings: function(settings) {
this.state.username = settings.username;
},
toggleSettings: function() {
this.state.settingsOpen = !this.state.settingsOpen;
},
toggleStarred: function(id) {
var msg = getMessage(parseInt(id));
msg.starred = !msg.starred;
},
render: function() {
var state = this.state;
var section;
if(state.selectedMessage) {
section = dom.div(
dom.h1('Message'),
dom.p(state.selectedMessage.text),
dom.a({ href: '#',
onClick: this.openItem.bind(null, null) },
'Back')
);
}
else {
section = dom.div(
{ className: 'feed' },
state.feed.items.length ?
dom.div({ className: 'feed-count' },
state.feed.items.length + ' message(s)') :
null,
dom.input({ onKeyUp: this.handleMessage.bind(this) }, ''),
Feed({
items: state.feed.items,
onOpenItem: this.openItem,
onStar: this.toggleStarred
})
);
}
return dom.div(
{ className: 'app' },
Toolbar({ username: state.username,
onSettings: this.toggleSettings }),
dom.div(
{ className: 'content' },
section
),
state.settingsOpen ?
Settings({ username: state.username,
onSave: this.updateSettings,
onClose: this.toggleSettings }) :
null
);
}
});
var Toolbar = Bloop.createClass({
render: function() {
return dom.div(
{ className: 'toolbar' },
dom.em('Logged in as ' + this.props.username),
dom.button({
onClick: this.props.onSettings
}, 'settings'),
dom.button({
onClick: undo
}, 'undo')
);
}
});
var Feed = Bloop.createClass({
render: function() {
return dom.div(
this.props.items.map(function(msg) {
return dom.div(
{ className: 'message',
onClick: this.props.onOpenItem.bind(null, msg.id) },
dom.h4(msg.author),
dom.div(msg.text),
dom.a({ href: '#',
onClick: function(e) {
e.stopPropagation();
this.props.onStar(msg.id);
}.bind(this),
className: 'star' },
dom.img({ src: msg.starred ? 'star.png' : 'star-outline.png' }))
);
}, this)
);
}
});
var Settings = Bloop.createClass({
getInitialState: function() {
return { username: this.props.username };
},
save: function(e) {
if(e) { e.preventDefault(); }
this.props.onSave({ username: this.state.username });
this.props.onClose();
},
handleUsername: function(e) {
this.state.username = e.target.value;
},
render: function() {
return dom.div(
null,
dom.div(
{ className: 'dismiss',
onClick: this.props.onClose
}
),
dom.div(
{ className: 'settings' },
dom.form(
{ onSubmit: this.save },
'Username: ',
dom.input({ value: this.state.username,
onChange: this.handleUsername }),
dom.div(
{ className: 'submit' },
dom.button({ type: 'submit',
onClick: this.save }, 'save')
)
)
)
);
}
});
var appState = {
username: 'James',
feed: { items: [
{ id: Math.random() * 10000 | 0,
author: 'James',
text: 'This is your initial message. Love it, embrace it.',
starred: false }
] },
selectedMessage: null,
settingsOpen: false
};
var app = App({state: appState});
// store
function getMessage(id) {
return _.find(appState.feed.items, { id: id });
}
// render
var prevStates = [JSON.stringify(appState)];
function undo() {
while(1) {
var state = JSON.parse(prevStates.pop());
if(!prevStates.length) {
// This is the initial app state, so unconditionally apply it
// and push it back onto the history so we don't lose it
appState.feed = state.feed;
prevStates.push(JSON.stringify(state));
break;
}
else if(JSON.stringify(appState.feed) !== JSON.stringify(state.feed)) {
// We found a state where the feed has changed, so apply it
appState.feed = state.feed;
break;
}
}
}
function render() {
app.state = appState;
var changed = Bloop.renderComponent(app, document.body);
if(changed) {
prevStates.push(JSON.stringify(appState));
}
requestAnimationFrame(render);
}
render();
var dom = Bloop.dom;
// components
var App = Bloop.createClass({
getInitialState: function() {
return { pageY: 0,
pageHeight: window.innerHeight };
},
componentDidRender: function() {
var numItems = this.props.items.length;
document.querySelector('.list').style.height = numItems * 31 + 'px';
var ul = document.querySelector('ul');
ul.style.top = this.state.pageY + 'px';
},
render: function() {
var pageY = this.state.pageY;
var begin = pageY / 31 | 0;
// Add 2 so that the top and bottom of the page are filled with
// next/prev item, not just whitespace if item not in full view
var end = begin + (this.state.pageHeight / 31 | 0 + 2);
var offset = pageY % 31;
return dom.div(
{ className: 'list',
style: 'position: relative; top: ' + (-offset) + 'px' },
dom.ul(
this.props.items.slice(begin, end).map(function(item) {
return dom.li(null, item.title);
})
)
);
}
});
// application code
var items = [];
for(var i=0; i<5000; i++) {
items.push({
title: 'Foo Bar ' + i
});
}
var app = App({ items: items });
window.addEventListener('scroll', function(e) {
app.state.pageY = Math.max(e.pageY, 0);
app.state.pageHeight = window.innerHeight;
});
// render
function render() {
Bloop.renderComponent(app, document.body);
requestAnimationFrame(render);
}
render();
var dom = Bloop.dom;
// components
var App = Bloop.createClass({
render: function() {
return dom.div(
dom.span(this.state.number.val()),
Toolbar({
number: this.state.number
})
);
}
});
var Toolbar = Bloop.createClass({
updateNumber: function(value) {
this.props.number.set(value);
},
render: function() {
return dom.div(
dom.button({
onClick: this.updateNumber.bind(null, this.props.number.val() - 1)
}, 'decrement'),
dom.button({
onClick: this.updateNumber.bind(null, this.props.number.val() + 1)
}, 'increment')
);
}
});
var data = { number: 0 };
var cortex = new Cortex(data, function() {
// Called whenever an update happened
Bloop.renderComponent(app, document.body);
});
// render
var app = App({ state: cortex });
Bloop.renderComponent(app, document.body);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.