Skip to content

Instantly share code, notes, and snippets.

@patrickml
Last active January 6, 2016 03:54
Show Gist options
  • Save patrickml/5fcfa09d22151a8fd0ce to your computer and use it in GitHub Desktop.
Save patrickml/5fcfa09d22151a8fd0ce to your computer and use it in GitHub Desktop.
Table = React.createClass({
mixins : [React.addons.PureRenderMixin],
/**
* Sets the components key and dir state
* @event the synthetic event from ReactJS
*/
setSortKey (event) {
//Get the key from the target element
let key = event.target.getAttribute('data-sortkey');
//Set the state
this.setState({
key : key, //Use the key from the element
dir : this.state.key === key ? -this.state.dir : 1 //check if we need to reverse sort
});
},
/**
* Compare to elements to check for sort order
* @a object 1 to be compared
* @b object 2 to be compared
*/
sortOrder (a, b) {
//Find the values using the current sort key
let aa = _.get(a, this.state.key),
bb = _.get(b, this.state.key);
//Check if we are compairing Strings
if(typeof aa === 'string' && typeof bb === 'string') {
aa = aa.toLowerCase();
bb = bb.toLowerCase();
}
// return aa.toLowerCase().localeCompare(bb.toLowerCase()) * this.state.dir;
//Compair numerical
if (aa < bb) return -1 * this.state.dir;
if (aa > bb) return 1 * this.state.dir;
return 0;
},
applyTransform (value, key, object) {
let transform = _.findWhere(this.props.transforms, { key : key });
return transform && transform.transform(value, object, this.props) || value;
},
/**
* @value The value of the cell
* @index the index used for Reacts Mapping
* @click the function that should be called on click
* @sortKey the key to sort by if clicked
* @className the classes that should be added
*/
renderCell (value, index, object, click, sortKey, className) {
return (
<td
key={ index }
onClick={ click }
data-sortkey={ sortKey }
className={ className }>
{ !sortKey && this.applyTransform(value, this.props.keys[index], object) || value }
</td>
);
},
/**
* Set the initial state of the component
*/
getInitialState () {
return {
key : this.props.keys[0],
dir : 1
};
},
/**
* Render a single table renderRow
* @row the json object to map
* @index the React Index
*/
renderRow (row, index) {
let instance = this;
return (
<tr key={ index }>
{
instance.props.keys.map(function (property, key) {
return instance.renderCell(_.get(row, property), key, row);
})
}
</tr>
);
},
/**
* Generates a css class for a header element base on sort
* @key checks the key of the element
*/
getSortClass (key) {
if(key === this.state.key) {
return ["sorted", this.state.dir === 1 ? 'asc' : 'desc'].join(" ");
}
return;
},
/**
* Renders a table header
*/
renderHeader () {
let instance = this;
return (
<thead>
<tr>
{
instance.props.header.map(function (value, key) {
return instance.renderCell(value, key, null, instance.setSortKey, instance.props.keys[key], instance.getSortClass(instance.props.keys[key]) );
})
}
</tr>
</thead>
);
},
/**
* Renders a html footer
*/
renderFooter () {
let instance = this;
return (
<tfoot>
<tr>
{
instance.props.footer.map(function (value, key) {
return instance.renderCell(value, key);
})
}
</tr>
</tfoot>
);
},
addCustomRow () {
let data = {};
_.each(this.refs, function (obj, key) {
let node = ReactDOM.findDOMNode(obj);
let raw = node.getAttribute('data-raw');
data[key] = raw && JSON.parse(raw) || node.value;
node.value = "";
node.removeAttribute('data-raw');
});
this.props.inlineCallback(data);
},
/**
* Adds the ability to add data inline with a table
*/
addInlineRow () {
let instance = this;
return (
<tr>
{
instance.props.keys.map(function (key, index) {
let comp = _.findWhere(instance.props.inlineComponents, { key : key });
if(index === instance.props.keys.length - 1) {
return <td key={ index }> <button className="button" onClick={ instance.addCustomRow }>Add</button> </td>;
}
return <td key={ index }> { React.cloneElement(comp && comp.component || <input type="text" />, {
ref : comp && comp.saveKey || key
}) } </td>;
})
}
</tr>
);
},
/**
* React render
*/
render () {
let instance = this;
if(instance.props.data && instance.props.data.length > 0 || instance.props.inlineComponents) {
return (
<table className="table">
{ instance.props.header && instance.renderHeader() }
<tbody>
{
instance.props.inlineComponents && instance.addInlineRow()
}
{
instance.props.data && instance.props.data.sort(instance.sortOrder).map(function (object, index) {
return object && instance.renderRow(object, index);
})
}
</tbody>
{ instance.props.footer && instance.renderFooter() }
</table>
);
} else {
return (
<div className="no-data">
<p>{ this.props.noDataString || "No Data" }</p>
</div>
);
}
}
});
RenderTemplate = React.createClass({
render () {
return (
<Table
data={ Collection.find().fetch() }
keys={[ 'name', '_id' ]}
header={ ['Name', '_id'] }
transforms={ [
{
key : '_id',
transform : function (value) {
return (<a href={ value }>{ value }</a>);
}
}
] }
/>
);
}
});
_.mixin({
get: function (obj, key) {
var type = typeof key;
if (type == 'string' || type == "number") {
key = ("" + key).replace(/\[(.*?)\]/,/\[(.*?)\]/, function (m, key) { //handle case where [1] may occur
return '.' + key.replace(/["']/g,/["']/g, ""); //strip quotes
}).split('.');
}
for (var i = 0, l = key.length; i < l; i++) {
if (typeof obj !== 'undefined' && _.has(obj, key[i])) obj = obj[key[i]];
else return undefined;
}
return obj;
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment