style and prepare boilerplate components

This commit is contained in:
Tim Daubenschütz 2015-05-29 16:28:05 +02:00
parent 9f255d7db7
commit 897b771068
18 changed files with 542 additions and 61 deletions

View File

@ -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 (
<div className={this.props.className}>
{this.props.children}
</div>
);
} else {
return (
<p>Loading</p>
);
}
}
});
export default AccordionList;

View File

@ -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 (
<div className={this.props.className}>
<div className="wrapper">
<div className="thumbnail-wrapper">
<img src={this.props.content.thumbnail} />
</div>
<div className="info-wrapper">
<h1>{this.props.content.title}</h1>
<h3>by {this.props.content.artist_name}</h3>
</div>
</div>
</div>
);
}
});
export default AccordionListItem;

View File

@ -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 (
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12 ascribe-table-header-row">
<div className="row">
{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 (
<TableHeaderItem
key={i}
columnClasses={columnClasses}
displayName={val.displayName}
columnName={columnName}
canBeOrdered={canBeOrdered}
orderAsc={this.props.orderAsc}
orderBy={this.props.orderBy}
changeOrder={this.props.changeOrder}>
</TableHeaderItem>
);
})}
</div>
</div>
);
}
});
export default TableHeader;

View File

@ -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 (
<div
className={this.props.columnClasses + ' ascribe-table-header-column'}
onClick={this.changeOrder}>
<span>{this.props.displayName} <TableHeaderItemCarret orderAsc={this.props.orderAsc} /></span>
</div>
);
} else {
return (
<div
className={this.props.columnClasses + ' ascribe-table-header-column'}
onClick={this.changeOrder}>
<span>{this.props.displayName}</span>
</div>
);
}
} else {
return (
<div className={this.props.columnClasses + ' ascribe-table-header-column'}>
<span>
{this.props.displayName}
</span>
</div>
);
}
}
});
export default TableHeaderItem;

View File

@ -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 (
<span className={'glyphicon ' + carretDirection}>
</span>
);
}
});
export default TableHeaderItemCarret;

View File

@ -0,0 +1,18 @@
import React from 'react';
let TableItemAcl = React.createClass({
propTypes: {
content: React.PropTypes.array.isRequired
},
render() {
return (
<span>
{this.props.content.join('/')}
</span>
);
}
});
export default TableItemAcl;

View File

@ -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 (
<span>
<img src={this.props.content} width="50" />
</span>
);
}
});
export default TableItemImg;

View File

@ -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 (
<TableItem
classNames={tableItemClasses + ' ' + this.props.className}
columnList={this.props.columnList}
columnContent={this.props.columnContent}
onClick={this.selectItem}>
</TableItem>
);
}
});
export default TableItemSelectable;

View File

@ -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 (
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<Table itemList={this.state.editionList[this.props.columnContent.id]} columnList={columnList}>
{this.state.editionList[this.props.columnContent.id].map((edition, i) => {
return (
<TableItemSelectable
className="ascribe-table-item-selectable"
selectItem={this.selectItem}
parentId={this.props.columnContent.id}
key={i}>
</TableItemSelectable>
);
})}
</Table>
</div>
</div>
);
}
};
return (
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12 ascribe-table-item">
<div className="row">
<TableItemWrapper
columnList={this.props.columnList}
columnContent={this.props.columnContent}
columnWidth={12}>
</TableItemWrapper>
<div className="col-xs-1 col-sm-1 col-md-1 col-lg-1 ascribe-table-item-column">
<TableItemSubtableButton content="+" onClick={this.loadEditionList}>
</TableItemSubtableButton>
</div>
</div>
{renderEditionListTable()}
</div>
);
}
});
export default TableItemSubtable;

View File

@ -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 (
<span>
<button type="button" className="btn btn-default btn-sm ascribe-table-expand-button" onClick={this.props.onClick}>
{this.props.content}
</button>
</span>
);
}
});
export default TableItemSubtableButton;

View File

@ -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 (
<span>
{this.props.content}
</span>
);
}
});
export default TableItemText;

View File

@ -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 (
<div>
{this.props.columnList.map((column, i) => {
let TypeElement = column.displayType;
let columnClass = this.calcColumnClasses(this.props.columnList, i, this.props.columnWidth);
return (
<div className={columnClass + ' ascribe-table-item-column'} key={i}>
<TypeElement content={this.props.columnContent[column.columnName]} width="50" />
</div>
);
})}
</div>
);
}
});
export default TableItemWrapper;

View File

@ -24,7 +24,7 @@ let Header = React.createClass({
render() {
return (
<nav className="navbar navbar-default navbar-fixed-top">
<nav className="navbar navbar-default">
<div className="container">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
@ -35,15 +35,12 @@ let Header = React.createClass({
</button>
<a className="navbar-brand" href="#">
<span>ascribe </span>
<span className="glyph-ascribe-spool-chunked"></span>
<span className="glyph-ascribe-spool-chunked ascribe-color"></span>
</a>
</div>
<div id="navbar" className="navbar-collapse collapse">
<ul className="nav navbar-nav navbar-left">
<li>
<Link to="pieces">Pieces</Link>
</li>
</ul>
<ul className="nav navbar-nav navbar-right">
<li className="dropdown">

View File

@ -4,14 +4,8 @@ import AltContainer from 'alt/AltContainer';
import PieceListStore from '../stores/piece_list_store';
import PieceListActions from '../actions/piece_list_actions';
import Table from './ascribe_table/table';
import TableItem from './ascribe_table/table_item';
import TableItemImg from './ascribe_table/table_item_img';
import TableItemText from './ascribe_table/table_item_text';
import TableItemSubtable from './ascribe_table/table_item_subtable';
import TableItemSubtableButton from './ascribe_table/table_item_subtable_button';
import TableColumnContentModel from '../models/table_column_content_model';
import AccordionList from './ascribe_accordion_list/accordion_list';
import AccordionListItem from './ascribe_accordion_list/accordion_list_item';
import Pagination from './ascribe_pagination/pagination';
@ -44,55 +38,43 @@ let PieceList = React.createClass({
this.state.orderAsc);
},
tableChangeOrder(orderBy, orderAsc) {
accordionChangeOrder(orderBy, orderAsc) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize,
this.state.search, orderBy, orderAsc);
},
render() {
let columnList = [
new TableColumnContentModel('thumbnail', '', TableItemImg, 2, false),
new TableColumnContentModel('artist_name', 'Artist', TableItemText, 4, true),
new TableColumnContentModel('title', 'Title', TableItemText, 4, true)
];
let currentPage = parseInt(this.props.query.page, 10) || 1;
let totalPages = Math.ceil(this.state.pieceListCount / this.state.pageSize)
if(this.state.pieceList && this.state.pieceList.length > 0) {
return (
<div>
<PieceListToolbar />
<Table
columnList={columnList}
changeOrder={this.tableChangeOrder}
itemList={this.state.pieceList}
itemListCount={this.state.pieceListCount}
orderBy={this.state.orderBy}
orderAsc={this.state.orderAsc}
search={this.state.search}
page={this.state.page}
pageSize={this.state.pageSize}>
{this.state.pieceList.map((item, i) => {
return (
<TableItemSubtable
key={i}>
</TableItemSubtable>
);
})}
</Table>
<Pagination currentPage={currentPage}
totalPages={totalPages}
goToPage={this.paginationGoToPage}>
</Pagination>
</div>
);
} else {
return (
<p>Loading</p>
);
}
return (
<div class="row">
<AccordionList
className="ascribe-accordion-list row"
changeOrder={this.accordionChangeOrder}
itemList={this.state.pieceList}
itemListCount={this.state.pieceListCount}
orderBy={this.state.orderBy}
orderAsc={this.state.orderAsc}
search={this.state.search}
page={this.state.page}
pageSize={this.state.pageSize}>
{this.state.pieceList.map((item, i) => {
return (
<AccordionListItem
className="col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 ascribe-accordion-list-item"
content={item}
key={i} />
);
})}
</AccordionList>
<Pagination
currentPage={currentPage}
totalPages={totalPages}
goToPage={this.paginationGoToPage}>
</Pagination>
</div>
);
}
});

View File

@ -0,0 +1,51 @@
$ascribe-accordion-list-item-height: 9em;
$ascribe-accordion-list-font: 'Source Sans Pro';
.ascribe-accordion-list-item {
background-color: rgba(0,0,0,0.004);
border: 1px solid black;
height: $ascribe-accordion-list-item-height;
margin-bottom: 3em;
padding-left:0;
padding-right:0;
overflow:hidden;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
border-left: 0.1em solid #E0E0E0;
border-right: 0.1em solid #E0E0E0;
border-top: 0.1em solid #E0E0E0;
border-radius: 5px;
border-bottom: 0.25em solid #E0E0E0;
.wrapper {
width:100%;
// ToDo: Include media queries for thumbnail
.thumbnail-wrapper {
float:left;
height:100%;
width:$ascribe-accordion-list-item-height;
overflow:hidden;
padding-left:0;
padding-right:0;
img {
height: $ascribe-accordion-list-item-height;
}
}
.info-wrapper {
float:left;
font-family: $ascribe-accordion-list-font;
margin-left: 2em;
padding-top: .75em;
h1 {
font-size: 2.25em;
}
h3 {
font-size: 1.1em;
margin: .7em 0 0 0;
}
}
}
}

View File

@ -0,0 +1 @@
$ascribe-color: rgba(2, 182, 163, 0.5);

View File

@ -1,16 +1,25 @@
// If you import a new .scss file, make sure to restart gulp
// otherwise it will not be included
@import 'variables';
@import 'ascribe-variables';
@import '../node_modules/bootstrap-sass/assets/stylesheets/bootstrap';
@import './ascribe-fonts/style';
@import './ascribe-fonts/ascribe-fonts';
#main {
padding-top: 70px;
}
@import 'ascribe-accordion_list';
.hidden {
display: none;
}
.navbar-default {
border-left:0;
border-right:0;
}
.ascribe-color {
color: $ascribe-color;
}
/* Taken from http://stackoverflow.com/a/20548578 */
.vcenter {
display: inline-block;

View File

@ -359,13 +359,13 @@ $container-lg: $container-large-desktop !default;
// Basics of a navbar
$navbar-height: 50px !default;
$navbar-margin-bottom: $line-height-computed !default;
$navbar-border-radius: $border-radius-base !default;
$navbar-border-radius: 0 !default;
$navbar-padding-horizontal: floor(($grid-gutter-width / 2)) !default;
$navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) !default;
$navbar-collapse-max-height: 340px !default;
$navbar-default-color: #777 !default;
$navbar-default-bg: #f8f8f8 !default;
$navbar-default-bg: white !default;
$navbar-default-border: darken($navbar-default-bg, 6.5%) !default;
// Navbar links
@ -378,7 +378,7 @@ $navbar-default-link-disabled-color: #ccc !default;
$navbar-default-link-disabled-bg: transparent !default;
// Navbar brand label
$navbar-default-brand-color: $navbar-default-link-color !default;
$navbar-default-brand-color: black !default;
$navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%) !default;
$navbar-default-brand-hover-bg: transparent !default;