Merge pull request #221 from aboodman/tagshow-urls

Tagshow urls
This commit is contained in:
Aaron Boodman
2015-08-25 14:58:02 -07:00
6 changed files with 110 additions and 74 deletions

View File

@@ -9,9 +9,9 @@ var DatasetPicker = React.createClass({
mixins: [ImmutableRenderMixin],
propTypes: {
root: React.PropTypes.instanceOf(noms.Ref).isRequired,
datasets: React.PropTypes.instanceOf(Promise).isRequired,
onChange: React.PropTypes.func.isRequired,
selected: React.PropTypes.instanceOf(Immutable.Map),
selected: React.PropTypes.string,
},
getInitialState: function() {
@@ -21,24 +21,21 @@ var DatasetPicker = React.createClass({
},
handleSelectChange: function(e) {
this.props.onChange(
this.state.datasets.find(
ds => ds.get('id') == e.target.value));
this.props.onChange(e.target.value);
},
render: function() {
// Get the datasets
this.props.root.deref().then(
heads => heads.first().deref()).then(
commit => commit.get('value').deref()).then(
dsRefs => Promise.all(dsRefs.map(ref => ref.deref()))).then(
this.props.datasets.then(
datasets => {
this.setState({datasets: Immutable.Set.of(...datasets)});
});
this.setState({
datasets: Immutable.Set.of(...datasets)
});
}
);
return <form>
Dataset:
<select value={this.props.selected.get('id')}
<select value={this.props.selected}
onChange={this.handleSelectChange}>
<option/>
{

View File

@@ -1,14 +1,31 @@
'use strict';
var Immutable = require('immutable');
var noms = require('noms');
var queryString = require('query-string');
var React = require('react');
var Root = require('./root.js');
noms.getRoot().then((rootRef) => {
noms.readValue(rootRef, noms.getChunk).then(render);
});
window.onload =
window.onhashchange = render;
function render(rootValue) {
var target = document.getElementById('root');
React.render(<Root rootValue={rootValue}/>, target);
function updateQuery(qs) {
location.hash = queryString.stringify(qs.toObject());
}
function render() {
var qs = queryString.parse(location.hash);
var target = document.getElementById('root');
// NOTE: This actually does a fetch, so if render() starts getting called
// more frequently (e.g., in response to window resize), then this should
// get moved someplace else.
var rootValue = noms.getRoot().then(
rootRef => noms.readValue(rootRef, noms.getChunk))
React.render(
<Root
qs={Immutable.Map(qs)}
rootValue={rootValue}
updateQuery={updateQuery}/>, target);
}

View File

@@ -1,6 +1,7 @@
{
"name": "noms-tagshow",
"dependencies": {
"query-string": "^2.4.0",
"react": "^0.12.0",
"react-immutable-render-mixin": "^0.8.1",
"immutable": "^3.7.4"

View File

@@ -8,50 +8,48 @@ var React = require('react');
var SlideShow = require('./slideshow.js');
var TagCloud = require('./tagcloud.js');
var tagCloudStyle = {
position: 'absolute',
left: 0,
top: 0,
var containerStyle = {
display: 'flex',
};
var slideShowStyle = {
position: 'absolute',
top: 0,
left: 300,
width: 500,
flex: 1,
};
var Root = React.createClass({
mixins: [ImmutableRenderMixin],
propTypes: {
rootValue: React.PropTypes.instanceOf(noms.Ref),
rootValue: React.PropTypes.instanceOf(Promise),
qs: React.PropTypes.instanceOf(Immutable.Map),
updateQuery: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
selectedDs: Immutable.Map(),
tags: Immutable.Map(),
selected: Immutable.Set(),
selectedPhotos: Immutable.Set(),
};
},
handleDataSetPicked: function(ds) {
this.setState({selectedDs: ds});
this.props.updateQuery(this.props.qs.set('ds', ds));
},
ds.get('heads').deref().then(
heads => heads.first().deref()).then(
commit => commit.get('value').deref()).then(
tags => this.setState({tags:tags}));
getSelectedTags: function() {
var tags = this.props.qs.get('tags');
if (tags) {
tags = tags.split(',');
} else {
tags = [];
}
return Immutable.Set(tags);
},
handleTagChoose: function(tag) {
this.setState({
selected: this.state.selected.has(tag) ?
this.state.selected.remove(tag) :
this.state.selected.add(tag),
});
var tags = this.getSelectedTags();
tags = tags.has(tag) ? tags.delete(tag) : tags.add(tag);
this.props.updateQuery(this.props.qs.set('tags', tags.toArray().join(',')));
},
render: function() {
@@ -60,32 +58,26 @@ var Root = React.createClass({
// transformations, or async functions, or something, everything has to be
// defensively deref'd.
// Get the selected photos
var selectedSetRefs = this.state.tags.filter(
(v, k) => this.state.selected.has(k)).valueSeq();
Promise.all(selectedSetRefs.map(r => r.deref())).then(
sets => {
this.setState({
selectedPhotos: Immutable.Set().union(...sets)
});
});
if (!this.props.qs.get('ds')) {
return <div>
<b>Error: </b> 'ds' hash parameter not found
</div>
}
return <div>
<DataSetPicker
root={this.props.rootValue}
selected={this.state.selectedDs}
onChange={this.handleDataSetPicked}/>
<br/>
<div style={{position:"relative"}}>
<div style={tagCloudStyle}>
<TagCloud tags={this.state.tags} selected={this.state.selected}
var dataset = noms.getDataset(this.props.qs.get('ds'))
.then(ref => ref.deref());
return (
<div style={containerStyle}>
<div>
<TagCloud ds={dataset} selected={this.getSelectedTags()}
onChoose={this.handleTagChoose}/>
</div>
<div style={slideShowStyle}>
<SlideShow photos={this.state.selectedPhotos}/>
<SlideShow ds={dataset} tags={this.getSelectedTags()}/>
</div>
</div>
</div>
);
},
});

View File

@@ -5,21 +5,39 @@ var ImmutableRenderMixin = require('react-immutable-render-mixin');
var noms = require('noms')
var React = require('react');
var containerStyle = {
};
var imageStyle = {
display: 'block',
width: '100%',
maxHeight: 300,
marginRight: 7,
};
var SlideShow = React.createClass({
mixins: [ImmutableRenderMixin],
propTypes: {
photos: React.PropTypes.instanceOf(Immutable.Set),
ds: React.PropTypes.instanceOf(Immutable.Map),
tags: React.PropTypes.instanceOf(Immutable.Set),
},
getInitialState: function() {
return {
photos: Immutable.Set(),
}
},
render: function() {
return <div>{
this.props.photos
this.props.ds
.then(head => head.get('value').deref())
.then(tags => Promise.all(
tags.filter((v, t) => this.props.tags.has(t))
.valueSeq()
.map(ref => ref.deref())))
.then(sets => this.setState({photos: Immutable.Set(...sets)}));
return <div style={containerStyle}>{
this.state.photos
.sort(
// This sorts the photos deterministically, by the ref of their image
// blob.

View File

@@ -12,22 +12,33 @@ var TagCloud = React.createClass({
mixins: [ImmutableRenderMixin],
propTypes: {
tags: React.PropTypes.instanceOf(Immutable.Map),
ds: React.PropTypes.instanceOf(Immutable.Map),
selected: React.PropTypes.instanceOf(Immutable.Set),
onChoose: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
tags: Immutable.Map(),
};
},
render: function() {
this.props.ds
.then(head => head.get('value').deref())
.then(tags => this.setState({tags: tags}));
return <div>{
this.props.tags.keySeq().sort().map(
(tag) => {
var ref = this.props.tags.get(tag);
var id = "tc-" + tag;
this.state.tags.keySeq().sort().map(
tag => {
var ref = this.state.tags.get(tag);
return <div style={buttonStyle}>
<input type="checkbox" name="tc" id={id}
checked={this.props.selected.has(tag)}
onChange={() => this.props.onChoose(tag) }/>
<label htmlFor={id}>{tag}</label>
<label>
<input type="checkbox" name="tc"
checked={this.props.selected.has(tag)}
onChange={() => this.props.onChoose(tag) }/>
{tag}
</label>
</div>
}).toArray()
}</div>