diff --git a/js/components/ascribe_accordion_list/accordion_list.js b/js/components/ascribe_accordion_list/accordion_list.js
new file mode 100644
index 00000000..9eb484b7
--- /dev/null
+++ b/js/components/ascribe_accordion_list/accordion_list.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import ReactAddons from 'react/addons';
+
+
+let AccordionList = React.createClass({
+ propTypes: {
+ className: React.PropTypes.string,
+ children: React.PropTypes.arrayOf(React.PropTypes.element).isRequired
+ },
+
+ render() {
+ if(this.props.itemList && this.props.itemList.length > 0) {
+ return (
+
+ {this.props.children}
+
+ );
+ } else {
+ return (
+ Loading
+ );
+ }
+ }
+});
+
+export default AccordionList;
\ No newline at end of file
diff --git a/js/components/ascribe_accordion_list/accordion_list_item.js b/js/components/ascribe_accordion_list/accordion_list_item.js
new file mode 100644
index 00000000..cf615820
--- /dev/null
+++ b/js/components/ascribe_accordion_list/accordion_list_item.js
@@ -0,0 +1,27 @@
+import React from 'react';
+
+let AccordionListItem = React.createClass({
+ propTypes: {
+ className: React.PropTypes.string,
+ content: React.PropTypes.object
+ },
+
+ render() {
+ console.log(this.props.content);
+ return (
+
+
+
+
+
+
+
{this.props.content.title}
+ by {this.props.content.artist_name}
+
+
+
+ );
+ }
+});
+
+export default AccordionListItem;
diff --git a/js/components/ascribe_accordion_list/table_header.js b/js/components/ascribe_accordion_list/table_header.js
new file mode 100644
index 00000000..8176ad31
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_header.js
@@ -0,0 +1,50 @@
+import React from 'react';
+
+import TableColumnMixin from '../../mixins/table_column_mixin';
+import GeneralUtils from '../../utils/general_utils';
+import TableHeaderItem from './table_header_item';
+
+import TableColumnContentModel from '../../models/table_column_content_model';
+
+
+let TableHeader = React.createClass({
+ mixins: [TableColumnMixin],
+ propTypes: {
+ columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)),
+ itemList: React.PropTypes.array.isRequired,
+ changeOrder: React.PropTypes.func,
+ orderAsc: React.PropTypes.bool,
+ orderBy: React.PropTypes.string
+ },
+
+ render() {
+ return (
+
+
+ {this.props.columnList.map((val, i) => {
+
+ let columnClasses = this.calcColumnClasses(this.props.columnList, i, 12);
+ let columnName = this.props.columnList[i].columnName;
+ let canBeOrdered = this.props.columnList[i].canBeOrdered;
+
+ return (
+
+
+ );
+ })}
+
+
+ );
+
+ }
+});
+
+export default TableHeader;
diff --git a/js/components/ascribe_accordion_list/table_header_item.js b/js/components/ascribe_accordion_list/table_header_item.js
new file mode 100644
index 00000000..dce2261e
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_header_item.js
@@ -0,0 +1,52 @@
+import React from 'react';
+
+import TableHeaderItemCarret from './table_header_item_carret';
+
+let TableHeaderItem = React.createClass({
+
+ propTypes: {
+ columnClasses: React.PropTypes.string.isRequired,
+ displayName: React.PropTypes.string.isRequired,
+ columnName: React.PropTypes.string.isRequired,
+ canBeOrdered: React.PropTypes.bool,
+ changeOrder: React.PropTypes.func,
+ orderAsc: React.PropTypes.bool,
+ orderBy: React.PropTypes.string
+ },
+
+ changeOrder() {
+ this.props.changeOrder(this.props.columnName, !this.props.orderAsc);
+ },
+
+ render() {
+ if(this.props.canBeOrdered && this.props.changeOrder && this.props.orderAsc != null && this.props.orderBy) {
+ if(this.props.columnName === this.props.orderBy) {
+ return (
+
+
{this.props.displayName}
+
+ );
+ } else {
+ return (
+
+ {this.props.displayName}
+
+ );
+ }
+ } else {
+ return (
+
+
+ {this.props.displayName}
+
+
+ );
+ }
+ }
+});
+
+export default TableHeaderItem;
diff --git a/js/components/ascribe_accordion_list/table_header_item_carret.js b/js/components/ascribe_accordion_list/table_header_item_carret.js
new file mode 100644
index 00000000..628ead11
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_header_item_carret.js
@@ -0,0 +1,24 @@
+import React from 'react';
+
+let TableHeaderItemCarret = React.createClass({
+ propTypes: {
+ orderAsc: React.PropTypes.bool.isRequired
+ },
+
+ render() {
+ let carretDirection = 'glyphicon-triangle-';
+
+ if(this.props.orderAsc) {
+ carretDirection += 'bottom';
+ } else {
+ carretDirection += 'top';
+ }
+
+ return (
+
+
+ );
+ }
+});
+
+export default TableHeaderItemCarret;
\ No newline at end of file
diff --git a/js/components/ascribe_accordion_list/table_item_acl.js b/js/components/ascribe_accordion_list/table_item_acl.js
new file mode 100644
index 00000000..87e14761
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_item_acl.js
@@ -0,0 +1,18 @@
+import React from 'react';
+
+
+let TableItemAcl = React.createClass({
+ propTypes: {
+ content: React.PropTypes.array.isRequired
+ },
+
+ render() {
+ return (
+
+ {this.props.content.join('/')}
+
+ );
+ }
+});
+
+export default TableItemAcl;
diff --git a/js/components/ascribe_accordion_list/table_item_img.js b/js/components/ascribe_accordion_list/table_item_img.js
new file mode 100644
index 00000000..c082e85a
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_item_img.js
@@ -0,0 +1,20 @@
+import React from 'react';
+
+/**
+ * This could be enhanced further by specifying an optional description for example
+ */
+let TableItemImg = React.createClass({
+ propTypes: {
+ content: React.PropTypes.string.isRequired,
+ },
+
+ render() {
+ return (
+
+
+
+ );
+ }
+});
+
+export default TableItemImg;
\ No newline at end of file
diff --git a/js/components/ascribe_accordion_list/table_item_selectable.js b/js/components/ascribe_accordion_list/table_item_selectable.js
new file mode 100644
index 00000000..d62f8d04
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_item_selectable.js
@@ -0,0 +1,38 @@
+import React from 'react';
+import classNames from 'classnames';
+
+import TableColumnContentModel from '../../models/table_column_content_model';
+
+import TableItem from './table_item';
+
+// This component is implemented as recommended here: http://stackoverflow.com/a/25723635/1263876
+let TableItemSelectable = React.createClass({
+
+ propTypes: {
+ columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)),
+ columnContent: React.PropTypes.object,
+ parentId: React.PropTypes.number,
+ className: React.PropTypes.string
+ },
+
+ selectItem() {
+ this.props.selectItem(this.props.parentId, this.props.columnContent.edition_number);
+ },
+
+ render() {
+ let tableItemClasses = classNames({
+ 'ascribe-table-item-selected': this.props.columnContent.selected
+ });
+
+ return (
+
+
+ );
+ }
+});
+
+export default TableItemSelectable;
diff --git a/js/components/ascribe_accordion_list/table_item_subtable.js b/js/components/ascribe_accordion_list/table_item_subtable.js
new file mode 100644
index 00000000..dcd3da0f
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_item_subtable.js
@@ -0,0 +1,110 @@
+import React from 'react';
+
+import TableColumnContentModel from '../../models/table_column_content_model';
+
+import EditionListStore from '../../stores/edition_list_store';
+import EditionListActions from '../../actions/edition_list_actions';
+
+
+import Table from './table';
+import TableItemWrapper from './table_item_wrapper';
+import TableItemText from './table_item_text';
+import TableItemAcl from './table_item_acl';
+import TableItemSelectable from './table_item_selectable';
+import TableItemSubtableButton from './table_item_subtable_button';
+
+
+let TableItemSubtable = React.createClass({
+ propTypes: {
+ columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)),
+ columnContent: React.PropTypes.object
+ },
+
+ getInitialState() {
+ return {
+ 'open': false
+ };
+ },
+
+ onChange(state) {
+ this.setState(state);
+ },
+
+ componentDidMount() {
+ EditionListStore.listen(this.onChange);
+ },
+
+ loadEditionList() {
+ if(this.state.open) {
+ this.setState({
+ 'open': false
+ });
+ } else {
+
+ EditionListActions.fetchEditionList(this.props.columnContent.id);
+ this.setState({
+ 'open': true,
+ 'editionList': EditionListStore.getState()
+ });
+ }
+ },
+
+ selectItem(parentId, itemId) {
+ EditionListActions.selectEdition({
+ 'pieceId': parentId,
+ 'editionId': itemId
+ });
+ },
+
+ render() {
+
+ let renderEditionListTable = () => {
+
+ let columnList = [
+ new TableColumnContentModel('edition_number', 'Number', TableItemText, 2, false),
+ new TableColumnContentModel('user_registered', 'User', TableItemText, 4, true),
+ new TableColumnContentModel('acl', 'Actions', TableItemAcl, 4, true)
+ ];
+
+ if(this.state.open && this.state.editionList[this.props.columnContent.id] && this.state.editionList[this.props.columnContent.id].length) {
+ return (
+
+
+
+ {this.state.editionList[this.props.columnContent.id].map((edition, i) => {
+ return (
+
+
+ );
+ })}
+
+
+
+ );
+ }
+ };
+
+ return (
+
+
+ {renderEditionListTable()}
+
+ );
+ }
+});
+
+export default TableItemSubtable;
diff --git a/js/components/ascribe_accordion_list/table_item_subtable_button.js b/js/components/ascribe_accordion_list/table_item_subtable_button.js
new file mode 100644
index 00000000..8c5431d8
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_item_subtable_button.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+let TableItemSubtableButton = React.createClass({
+
+ propTypes: {
+ content: React.PropTypes.string.isRequired,
+ onClick: React.PropTypes.func.isRequired
+ },
+
+ render() {
+ return (
+
+
+
+ );
+ }
+});
+
+export default TableItemSubtableButton;
\ No newline at end of file
diff --git a/js/components/ascribe_accordion_list/table_item_text.js b/js/components/ascribe_accordion_list/table_item_text.js
new file mode 100644
index 00000000..0e90a2d5
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_item_text.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+
+let TableItemText = React.createClass({
+ propTypes: {
+ content: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.number
+ ])
+ },
+
+ render() {
+ return (
+
+ {this.props.content}
+
+ );
+ }
+});
+
+export default TableItemText;
diff --git a/js/components/ascribe_accordion_list/table_item_wrapper.js b/js/components/ascribe_accordion_list/table_item_wrapper.js
new file mode 100644
index 00000000..2f9f4c7e
--- /dev/null
+++ b/js/components/ascribe_accordion_list/table_item_wrapper.js
@@ -0,0 +1,34 @@
+import React from 'react';
+
+import TableColumnContentModel from '../../models/table_column_content_model';
+import TableColumnMixin from '../../mixins/table_column_mixin';
+
+let TableItemWrapper = React.createClass({
+ mixins: [TableColumnMixin],
+ propTypes: {
+ columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)),
+ columnContent: React.PropTypes.object,
+ columnWidth: React.PropTypes.number.isRequired
+ },
+
+ render() {
+ return (
+
+ {this.props.columnList.map((column, i) => {
+
+ let TypeElement = column.displayType;
+ let columnClass = this.calcColumnClasses(this.props.columnList, i, this.props.columnWidth);
+
+ return (
+
+
+
+ );
+
+ })}
+
+ );
+ }
+});
+
+export default TableItemWrapper;
\ No newline at end of file
diff --git a/js/components/header.js b/js/components/header.js
index f191c04c..20855b1b 100644
--- a/js/components/header.js
+++ b/js/components/header.js
@@ -24,7 +24,7 @@ let Header = React.createClass({
render() {
return (
-