1
0
mirror of https://github.com/ascribe/onion.git synced 2025-01-03 10:25:08 +01:00

Merge branch 'AD-51-refactor-table-to-mockup'

This commit is contained in:
Tim Daubenschütz 2015-06-02 15:37:26 +02:00
commit ed2adb324b
17 changed files with 424 additions and 154 deletions

View File

@ -9,7 +9,7 @@
<link rel="stylesheet" href="//brick.a.ssl.fastly.net/Source+Sans+Pro:400,600,700,900"> <link rel="stylesheet" href="//brick.a.ssl.fastly.net/Source+Sans+Pro:400,600,700,900">
</head> </head>
<body> <body>
<div id="main" class="container clear-margins-and-paddings"></div> <div id="main" class="container clear-paddings"></div>
<div id="modal" class="container"></div> <div id="modal" class="container"></div>
<script src="build/app.js"></script> <script src="build/app.js"></script>
</body> </body>

View File

@ -6,7 +6,8 @@ import PieceListFetcher from '../fetchers/piece_list_fetcher';
class PieceListActions { class PieceListActions {
constructor() { constructor() {
this.generateActions( this.generateActions(
'updatePieceList' 'updatePieceList',
'showEditionList'
); );
} }

View File

@ -6,30 +6,17 @@ import TableItem from '../ascribe_table/table_item';
import TableColumnContentModel from '../../models/table_column_content_model'; import TableColumnContentModel from '../../models/table_column_content_model';
let AccordionListItemTable = React.createClass({ let AccordionListItemTable = React.createClass({
getInitialState() {
return {
'show': false
};
},
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
parentId: React.PropTypes.number, parentId: React.PropTypes.number,
fetchData: React.PropTypes.func,
itemList: React.PropTypes.array, itemList: React.PropTypes.array,
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)),
numOfTableItems: React.PropTypes.number numOfTableItems: React.PropTypes.number,
}, show: React.PropTypes.bool
toggleTable() {
this.props.fetchData();
this.setState({
'show': !this.state.show
});
}, },
render() { render() {
if(this.props.itemList && this.state.show) { if(this.props.show && this.props.itemList) {
return ( return (
<div className={this.props.className}> <div className={this.props.className}>
<Table <Table
@ -44,44 +31,17 @@ let AccordionListItemTable = React.createClass({
); );
})} })}
</Table> </Table>
<AccordionListItemTableToggle {this.props.children}
className="ascribe-accordion-list-table-toggle"
onClick={this.toggleTable}
show={this.state.show}
numOfTableItems={this.props.numOfTableItems} />
</div> </div>
); );
} else { } else {
return ( return (
<div className={this.props.className}> <div className={this.props.className}>
<AccordionListItemTableToggle {this.props.children}
className="ascribe-accordion-list-table-toggle"
onClick={this.toggleTable}
show={this.state.show}
numOfTableItems={this.props.numOfTableItems} />
</div> </div>
); );
} }
} }
}); });
let AccordionListItemTableToggle = React.createClass({
propTypes: {
className: React.PropTypes.string,
onClick: React.PropTypes.func,
show: React.PropTypes.bool,
numOfTableItems: React.PropTypes.number
},
render() {
return (
<span
className={this.props.className}
onClick={this.props.onClick}>
{this.props.show ? 'Hide all ' + this.props.numOfTableItems + ' Editions' : 'Show all ' + this.props.numOfTableItems + ' Editions'}
</span>
);
}
});
export default AccordionListItemTable; export default AccordionListItemTable;

View File

@ -2,8 +2,11 @@ import React from 'react';
import EditionListStore from '../../stores/edition_list_store'; import EditionListStore from '../../stores/edition_list_store';
import EditionListActions from '../../actions/edition_list_actions'; import EditionListActions from '../../actions/edition_list_actions';
import PieceListStore from '../../stores/piece_list_store';
import PieceListActions from '../../actions/piece_list_actions';
import AccordionListItemTable from './accordion_list_item_table'; import AccordionListItemTable from './accordion_list_item_table';
import AccordionListItemTableToggle from './accordion_list_item_table_toggle';
import TableColumnContentModel from '../../models/table_column_content_model'; import TableColumnContentModel from '../../models/table_column_content_model';
@ -17,7 +20,8 @@ let AccordionListItemTableEditions = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
parentId: React.PropTypes.number, parentId: React.PropTypes.number,
numOfEditions: React.PropTypes.number numOfEditions: React.PropTypes.number,
show: React.PropTypes.bool
}, },
getInitialState() { getInitialState() {
@ -36,14 +40,15 @@ let AccordionListItemTableEditions = React.createClass({
EditionListStore.unlisten(this.onChange); EditionListStore.unlisten(this.onChange);
}, },
getEditionList() {
EditionListActions.fetchEditionList(this.props.parentId);
},
selectItem(pieceId, editionId) { selectItem(pieceId, editionId) {
EditionListActions.selectEdition({pieceId, editionId}); EditionListActions.selectEdition({pieceId, editionId});
}, },
toggleTable() {
PieceListActions.showEditionList(this.props.parentId);
EditionListActions.fetchEditionList(this.props.parentId);
},
render() { render() {
let columnList = [ let columnList = [
new TableColumnContentModel((item) => { return { 'editionId': item.id, 'pieceId': this.props.parentId, 'selectItem': this.selectItem, 'selected': item.selected }}, '', '', TableItemCheckbox, 1, false), new TableColumnContentModel((item) => { return { 'editionId': item.id, 'pieceId': this.props.parentId, 'selectItem': this.selectItem, 'selected': item.selected }}, '', '', TableItemCheckbox, 1, false),
@ -53,13 +58,22 @@ let AccordionListItemTableEditions = React.createClass({
]; ];
return ( return (
<AccordionListItemTable <div>
className={this.props.className} <AccordionListItemTable
parentId={this.props.parentId} className={this.props.className}
fetchData={this.getEditionList} parentId={this.props.parentId}
itemList={this.state.editionList[this.props.parentId]} itemList={this.state.editionList[this.props.parentId]}
columnList={columnList} columnList={columnList}
numOfTableItems={this.props.numOfEditions} /> numOfTableItems={this.props.numOfEditions}
show={this.props.show}>
<AccordionListItemTableToggle
className="ascribe-accordion-list-table-toggle"
onClick={this.toggleTable}
show={this.props.show}
numOfTableItems={this.props.numOfEditions} />
</AccordionListItemTable>
</div>
); );
} }
}); });

View File

@ -0,0 +1,22 @@
import React from 'react';
let AccordionListItemTableToggle = React.createClass({
propTypes: {
className: React.PropTypes.string,
onClick: React.PropTypes.func,
show: React.PropTypes.bool,
numOfTableItems: React.PropTypes.number
},
render() {
return (
<span
className={this.props.className}
onClick={this.props.onClick}>
{this.props.show ? 'Hide all ' + this.props.numOfTableItems + ' Editions' : 'Show all ' + this.props.numOfTableItems + ' Editions'}
</span>
);
}
});
export default AccordionListItemTableToggle;

View File

@ -0,0 +1,117 @@
import React from 'react';
import EditionListStore from '../../stores/edition_list_store';
import EditionListActions from '../../actions/edition_list_actions';
import AclButton from '../acl_button';
import PieceListBulkModalSelectedEditionsWidget from './piece_list_bulk_modal_selected_editions_widget';
let PieceListBulkModal = React.createClass({
propTypes: {
className: React.PropTypes.string
},
getInitialState() {
return EditionListStore.getState();
},
onChange(state) {
this.setState(state);
},
componentDidMount() {
EditionListStore.listen(this.onChange)
},
componentDidUnmount() {
EditionListStore.unlisten(this.onChange)
},
filterForSelected(edition) {
return edition.selected;
},
fetchSelectedEditionList() {
let selectedEditionList = [];
Object
.keys(this.state.editionList)
.forEach((key) => {
let filteredEditionsForPiece = this.state.editionList[key].filter(this.filterForSelected);
selectedEditionList = selectedEditionList.concat(filteredEditionsForPiece);
});
return selectedEditionList;
},
intersectAcls(a, b) {
return a.filter((val) => b.indexOf(val) > -1);
},
bulk(action) {
console.log(action);
},
getAvailableAcls() {
let availableAcls = [];
let selectedEditionList = this.fetchSelectedEditionList();
// If no edition has been selected, availableActions is empty
// If only one edition has been selected, their actions are available
// If more than one editions have been selected, their acl properties are intersected
if(selectedEditionList.length >= 1) {
availableAcls = selectedEditionList[0].acl;
}
if(selectedEditionList.length >= 2) {
for(let i = 1; i < selectedEditionList.length; i++) {
availableAcls = this.intersectAcls(availableAcls, selectedEditionList[i].acl);
}
}
return availableAcls;
},
clearAllSelections() {
EditionListActions.clearAllEditionSelections();
},
render() {
let availableAcls = this.getAvailableAcls();
if(availableAcls.length > 0) {
return (
<div className={this.props.className}>
<div className="row no-margin">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12 piece-list-bulk-modal">
<p></p>
<div className="row">
<div className="text-center">
<PieceListBulkModalSelectedEditionsWidget
numberOfSelectedEditions={this.fetchSelectedEditionList().length} />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span
className="piece-list-bulk-modal-clear-all"
onClick={this.clearAllSelections}>clear all</span>
</div>
</div>
<p></p>
<div className="row">
<div className="text-center">
<AclButton availableAcls={availableAcls} action="transfer" actionFunction={this.bulk} />
<AclButton availableAcls={availableAcls} action="consign" actionFunction={this.bulk} />
<AclButton availableAcls={availableAcls} action="share" actionFunction={this.bulk} />
<AclButton availableAcls={availableAcls} action="loan" actionFunction={this.bulk} />
</div>
</div>
</div>
</div>
</div>
);
} else {
return null;
}
}
});
export default PieceListBulkModal;

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
let PieceListToolbarSelectedEditionsWidget = React.createClass({ let PieceListBulkModalSelectedEditionsWidget = React.createClass({
propTypes: { propTypes: {
numberOfSelectedEditions: React.PropTypes.number.isRequired numberOfSelectedEditions: React.PropTypes.number.isRequired
}, },
@ -14,4 +14,4 @@ let PieceListToolbarSelectedEditionsWidget = React.createClass({
} }
}); });
export default PieceListToolbarSelectedEditionsWidget; export default PieceListBulkModalSelectedEditionsWidget;

View File

@ -1,18 +1,17 @@
import React from 'react'; import React from 'react';
import EditionListStore from '../../stores/edition_list_store'; import PieceListStore from '../../stores/piece_list_store';
import EditionListActions from '../../actions/edition_list_actions'; import PieceListActions from '../../actions/piece_list_actions';
import AclButton from '../acl_button'; import Input from 'react-bootstrap/lib/Input';
import PieceListToolbarSelectedEditionsWidget from './piece_list_toolbar_selected_editions_widget'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import PieceListToolbarFilterWidgetFilter from './piece_list_toolbar_filter_widget';
let PieceListToolbar = React.createClass({ let PieceListToolbar = React.createClass({
propTypes: {
className: React.PropTypes.string
},
getInitialState() { getInitialState() {
return EditionListStore.getState(); return PieceListStore.getState();
}, },
onChange(state) { onChange(state) {
@ -20,97 +19,38 @@ let PieceListToolbar = React.createClass({
}, },
componentDidMount() { componentDidMount() {
EditionListStore.listen(this.onChange) PieceListStore.listen(this.onChange);
}, },
componentDidUnmount() { componentDidUnmount() {
EditionListStore.unlisten(this.onChange) PieceListStore.unlisten(this.onChange);
}, },
filterForSelected(edition) { searchFor() {
return edition.selected; let searchTerm = this.refs.search.getInputDOMNode().value;
}, PieceListActions.fetchPieceList(this.state.page, this.pageSize, searchTerm, this.state.orderBy, this.state.orderAsc);
fetchSelectedEditionList() {
let selectedEditionList = [];
Object
.keys(this.state.editionList)
.forEach((key) => {
let filteredEditionsForPiece = this.state.editionList[key].filter(this.filterForSelected);
selectedEditionList = selectedEditionList.concat(filteredEditionsForPiece);
});
return selectedEditionList;
},
intersectAcls(a, b) {
return a.filter((val) => b.indexOf(val) > -1);
},
bulk(action) {
console.log(action);
},
getAvailableAcls() {
let availableAcls = [];
let selectedEditionList = this.fetchSelectedEditionList();
// If no edition has been selected, availableActions is empty
// If only one edition has been selected, their actions are available
// If more than one editions have been selected, their acl properties are intersected
if(selectedEditionList.length >= 1) {
availableAcls = selectedEditionList[0].acl;
}
if(selectedEditionList.length >= 2) {
for(let i = 1; i < selectedEditionList.length; i++) {
availableAcls = this.intersectAcls(availableAcls, selectedEditionList[i].acl);
}
}
return availableAcls;
},
clearAllSelections() {
EditionListActions.clearAllEditionSelections();
}, },
render() { render() {
let availableAcls = this.getAvailableAcls(); let searchIcon = <Glyphicon glyph='search' />;
if(availableAcls.length > 0) { return (
return ( <div className={this.props.className}>
<div className={this.props.className}> <div className="row">
<div className="row no-margin"> <div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12 piece-list-toolbar"> <div className="row">
<p></p> <div className="col-xs-12 col-md-12 col-md-5 col-lg-4 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 clear-paddings">
<div className="row"> <div className="form-inline">
<div className="text-center"> <Input type='text' ref="search" placeholder="Search..." onChange={this.searchFor} addonAfter={searchIcon} />
<PieceListToolbarSelectedEditionsWidget &nbsp;&nbsp;
numberOfSelectedEditions={this.fetchSelectedEditionList().length} /> {/*<PieceListToolbarFilterWidgetFilter />*/}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span
className="piece-list-toolbar-clear-all"
onClick={this.clearAllSelections}>clear all</span>
</div>
</div>
<p></p>
<div className="row">
<div className="text-center">
<AclButton availableAcls={availableAcls} action="transfer" actionFunction={this.bulk} />
<AclButton availableAcls={availableAcls} action="consign" actionFunction={this.bulk} />
<AclButton availableAcls={availableAcls} action="share" actionFunction={this.bulk} />
<AclButton availableAcls={availableAcls} action="loan" actionFunction={this.bulk} />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); </div>
} else { );
return null;
}
} }
}); });

View File

@ -0,0 +1,31 @@
import React from 'react';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import MenuItem from 'react-bootstrap/lib/MenuItem';
let PieceListToolbarFilterWidgetFilter = React.createClass({
render() {
let filterIcon = <Glyphicon glyph='filter' />;
return (
<DropdownButton title={filterIcon}>
<li style={{'textAlign': 'center'}}>
<em>Show Pieces that:</em>
</li>
<MenuItem eventKey='1'>
<div className="checkbox">
I can transfer <input type="checkbox" />
</div>
</MenuItem>
<MenuItem eventKey='2'>
<div className="checkbox">
I can consign <input type="checkbox" />
</div>
</MenuItem>
</DropdownButton>
);
}
});
export default PieceListToolbarFilterWidgetFilter;

View File

@ -10,6 +10,7 @@ import AccordionListItemTableEditions from './ascribe_accordion_list/accordion_l
import Pagination from './ascribe_pagination/pagination'; import Pagination from './ascribe_pagination/pagination';
import PieceListBulkModal from './ascribe_piece_list_bulk_modal/piece_list_bulk_modal';
import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar'; import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
@ -51,6 +52,7 @@ let PieceList = React.createClass({
return ( return (
<div> <div>
<PieceListToolbar className="ascribe-piece-list-toolbar" /> <PieceListToolbar className="ascribe-piece-list-toolbar" />
<PieceListBulkModal className="ascribe-piece-list-bulk-modal" />
<AccordionList <AccordionList
className="ascribe-accordion-list" className="ascribe-accordion-list"
changeOrder={this.accordionChangeOrder} changeOrder={this.accordionChangeOrder}
@ -68,7 +70,8 @@ let PieceList = React.createClass({
key={i}> key={i}>
<AccordionListItemTableEditions <AccordionListItemTableEditions
className="ascribe-accordion-list-item-table col-xs-6 col-sm-6 col-md-6 col-lg-6 col-xs-offset-3 col-sm-offset-3 col-md-offset-3 col-lg-offset-3" className="ascribe-accordion-list-item-table col-xs-6 col-sm-6 col-md-6 col-lg-6 col-xs-offset-3 col-sm-offset-3 col-md-offset-3 col-lg-offset-3"
parentId={item.id} parentId={item.id}
show={item.show}
numOfEditions={item.num_editions}/> numOfEditions={item.num_editions}/>
</AccordionListItem> </AccordionListItem>
); );

View File

@ -24,6 +24,19 @@ class PieceListStore {
this.bindActions(PieceListActions); this.bindActions(PieceListActions);
} }
onShowEditionList(pieceId) {
this.pieceList
.forEach((piece) => {
if(piece.id === pieceId) {
if(piece.show) {
piece.show = false;
} else {
piece.show = true;
}
}
});
}
onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount }) { onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount }) {
this.page = page; this.page = page;
this.pageSize = pageSize; this.pageSize = pageSize;

View File

@ -1,4 +1,4 @@
.ascribe-piece-list-toolbar { .ascribe-piece-list-bulk-modal {
position: fixed; position: fixed;
top:0; top:0;
width:1170px; width:1170px;
@ -14,7 +14,7 @@
z-index:9999; z-index:9999;
} }
.piece-list-toolbar-clear-all { .piece-list-bulk-modal-clear-all {
text-decoration: underline; text-decoration: underline;
cursor:pointer; cursor:pointer;
} }

View File

@ -0,0 +1,3 @@
.ascribe-piece-list-toolbar {
margin-bottom: 1.5em;
}

View File

@ -1,12 +1,14 @@
// If you import a new .scss file, make sure to restart gulp // If you import a new .scss file, make sure to restart gulp
// otherwise it will not be included // otherwise it will not be included
@import 'variables'; @import 'variables';
@import 'ascribe-variables'; @import 'ascribe_variables';
@import '../node_modules/bootstrap-sass/assets/stylesheets/bootstrap'; @import '../node_modules/bootstrap-sass/assets/stylesheets/bootstrap';
@import './ascribe-fonts/style'; @import './ascribe-fonts/style';
@import './ascribe-fonts/ascribe-fonts'; @import './ascribe-fonts/ascribe-fonts';
@import 'ascribe-accordion_list'; @import 'ascribe_accordion_list';
@import 'ascribe-piece-list-toolbar'; @import 'ascribe_piece_list_bulk_modal';
@import 'ascribe_piece_list_toolbar';
@import 'offset_right';
.hidden { .hidden {
display: none; display: none;
@ -15,10 +17,10 @@
.navbar-default { .navbar-default {
border-left:0; border-left:0;
border-right:0; border-right:0;
margin-bottom: 3em; margin-bottom: 1.5em;
} }
.clear-margins-and-paddings { .clear-paddings {
padding-left:0; padding-left:0;
padding-right:0; padding-right:0;
} }

164
sass/offset_right.scss Normal file
View File

@ -0,0 +1,164 @@
/* Taken from: http://stackoverflow.com/a/27501063/1263876 */
.col-xs-offset-right-12 {
margin-right: 100%;
}
.col-xs-offset-right-11 {
margin-right: 91.66666667%;
}
.col-xs-offset-right-10 {
margin-right: 83.33333333%;
}
.col-xs-offset-right-9 {
margin-right: 75%;
}
.col-xs-offset-right-8 {
margin-right: 66.66666667%;
}
.col-xs-offset-right-7 {
margin-right: 58.33333333%;
}
.col-xs-offset-right-6 {
margin-right: 50%;
}
.col-xs-offset-right-5 {
margin-right: 41.66666667%;
}
.col-xs-offset-right-4 {
margin-right: 33.33333333%;
}
.col-xs-offset-right-3 {
margin-right: 25%;
}
.col-xs-offset-right-2 {
margin-right: 16.66666667%;
}
.col-xs-offset-right-1 {
margin-right: 8.33333333%;
}
.col-xs-offset-right-0 {
margin-right: 0;
}
@media (min-width: 768px) {
.col-sm-offset-right-12 {
margin-right: 100%;
}
.col-sm-offset-right-11 {
margin-right: 91.66666667%;
}
.col-sm-offset-right-10 {
margin-right: 83.33333333%;
}
.col-sm-offset-right-9 {
margin-right: 75%;
}
.col-sm-offset-right-8 {
margin-right: 66.66666667%;
}
.col-sm-offset-right-7 {
margin-right: 58.33333333%;
}
.col-sm-offset-right-6 {
margin-right: 50%;
}
.col-sm-offset-right-5 {
margin-right: 41.66666667%;
}
.col-sm-offset-right-4 {
margin-right: 33.33333333%;
}
.col-sm-offset-right-3 {
margin-right: 25%;
}
.col-sm-offset-right-2 {
margin-right: 16.66666667%;
}
.col-sm-offset-right-1 {
margin-right: 8.33333333%;
}
.col-sm-offset-right-0 {
margin-right: 0;
}
}
@media (min-width: 992px) {
.col-md-offset-right-12 {
margin-right: 100%;
}
.col-md-offset-right-11 {
margin-right: 91.66666667%;
}
.col-md-offset-right-10 {
margin-right: 83.33333333%;
}
.col-md-offset-right-9 {
margin-right: 75%;
}
.col-md-offset-right-8 {
margin-right: 66.66666667%;
}
.col-md-offset-right-7 {
margin-right: 58.33333333%;
}
.col-md-offset-right-6 {
margin-right: 50%;
}
.col-md-offset-right-5 {
margin-right: 41.66666667%;
}
.col-md-offset-right-4 {
margin-right: 33.33333333%;
}
.col-md-offset-right-3 {
margin-right: 25%;
}
.col-md-offset-right-2 {
margin-right: 16.66666667%;
}
.col-md-offset-right-1 {
margin-right: 8.33333333%;
}
.col-md-offset-right-0 {
margin-right: 0;
}
}
@media (min-width: 1200px) {
.col-lg-offset-right-12 {
margin-right: 100%;
}
.col-lg-offset-right-11 {
margin-right: 91.66666667%;
}
.col-lg-offset-right-10 {
margin-right: 83.33333333%;
}
.col-lg-offset-right-9 {
margin-right: 75%;
}
.col-lg-offset-right-8 {
margin-right: 66.66666667%;
}
.col-lg-offset-right-7 {
margin-right: 58.33333333%;
}
.col-lg-offset-right-6 {
margin-right: 50%;
}
.col-lg-offset-right-5 {
margin-right: 41.66666667%;
}
.col-lg-offset-right-4 {
margin-right: 33.33333333%;
}
.col-lg-offset-right-3 {
margin-right: 25%;
}
.col-lg-offset-right-2 {
margin-right: 16.66666667%;
}
.col-lg-offset-right-1 {
margin-right: 8.33333333%;
}
.col-lg-offset-right-0 {
margin-right: 0;
}
}