1
0
mirror of https://github.com/ascribe/onion.git synced 2025-01-03 18:35:09 +01:00

Merge remote-tracking branch 'remotes/origin/AD-496-add-control-buttons-to-fineupload' into AD-368-harmonize-functionality-of-ascrib

This commit is contained in:
diminator 2015-07-01 11:22:49 +02:00
commit ca3b058b4d
25 changed files with 112 additions and 59 deletions

View File

@ -18,7 +18,10 @@ let AccordionList = React.createClass({
); );
} else if(this.props.itemList.length === 0) { } else if(this.props.itemList.length === 0) {
return ( return (
<p className="text-center">You don't have any works yet...</p> <div>
<p className="text-center">You don't have any works yet...</p>
<p className="text-center">To register one, click <a href="register_piece">here</a>!</p>
</div>
); );
} else { } else {
return ( return (

View File

@ -16,10 +16,10 @@ let AccordionListItem = React.createClass({
<div className="row"> <div className="row">
<div className={this.props.className}> <div className={this.props.className}>
<div className="wrapper"> <div className="wrapper">
<div className="col-xs-4 col-sm-4 col-md-4 col-lg-4 thumbnail-wrapper"> <div className="col-xs-5 col-sm-5 col-md-4 col-lg-4 thumbnail-wrapper">
<img src={this.props.content.thumbnail} /> <img src={this.props.content.thumbnail} />
</div> </div>
<div className="col-xs-7 col-sm-7 col-md-7 col-lg-7 col-xs-offset-1 col-sm-offset-1 col-md-offset-1 col-lg-offset-1"> <div className="col-xs-7 col-sm-7 col-md-7 col-lg-7 col-md-offset-1 col-lg-offset-1">
<h1>{this.props.content.title}</h1> <h1>{this.props.content.title}</h1>
<h3>{getLangText('by %s', this.props.content.artist_name)}</h3> <h3>{getLangText('by %s', this.props.content.artist_name)}</h3>
<h3>{this.props.content.date_created.split('-')[0]}</h3> <h3>{this.props.content.date_created.split('-')[0]}</h3>

View File

@ -24,6 +24,7 @@ let AccordionListItemTable = React.createClass({
return ( return (
<div className={this.props.className}> <div className={this.props.className}>
<Table <Table
responsive
className="ascribe-table" className="ascribe-table"
columnList={this.props.columnList} columnList={this.props.columnList}
itemList={this.props.itemList} itemList={this.props.itemList}

View File

@ -121,7 +121,7 @@ let AccordionListItemTableEditions = React.createClass({
'Edition', 'Edition',
TableItemText, TableItemText,
1, 1,
true, false,
transition transition
), ),
new ColumnModel( new ColumnModel(
@ -130,11 +130,12 @@ let AccordionListItemTableEditions = React.createClass({
'content': item.bitcoin_id 'content': item.bitcoin_id
}; }, }; },
'bitcoin_id', 'bitcoin_id',
getLangText('Bitcoin Address'), getLangText('ID'),
TableItemText, TableItemText,
5, 5,
true, false,
transition transition,
'hidden-xs visible-sm'
), ),
new ColumnModel( new ColumnModel(
(item) => { (item) => {

View File

@ -19,7 +19,8 @@ let AclButton = React.createClass({
availableAcls: React.PropTypes.array.isRequired, availableAcls: React.PropTypes.array.isRequired,
editions: React.PropTypes.array.isRequired, editions: React.PropTypes.array.isRequired,
currentUser: React.PropTypes.object, currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func.isRequired handleSuccess: React.PropTypes.func.isRequired,
className: React.PropTypes.string
}, },
actionProperties(){ actionProperties(){
@ -74,9 +75,9 @@ let AclButton = React.createClass({
return ( return (
<ModalWrapper <ModalWrapper
button={ button={
<div className={shouldDisplay ? 'btn btn-default btn-sm' : 'hidden'}> <button className={shouldDisplay ? 'btn btn-default btn-sm ' : 'hidden'}>
{this.props.action.toUpperCase()} {this.props.action.toUpperCase()}
</div> </button>
} }
handleSuccess={ aclProps.handleSuccess } handleSuccess={ aclProps.handleSuccess }
title={ aclProps.title } title={ aclProps.title }

View File

@ -41,7 +41,7 @@ let AclButtonList = React.createClass({
action="transfer" action="transfer"
editions={this.props.editions} editions={this.props.editions}
currentUser={this.state.currentUser} currentUser={this.state.currentUser}
handleSuccess={this.props.handleSuccess} /> handleSuccess={this.props.handleSuccess}/>
<AclButton <AclButton
availableAcls={this.props.availableAcls} availableAcls={this.props.availableAcls}
action="consign" action="consign"

View File

@ -97,12 +97,12 @@ let PieceListBulkModal = React.createClass({
</div> </div>
</div> </div>
<p></p> <p></p>
<div className="row"> <div className="row-fluid">
<AclButtonList <AclButtonList
availableAcls={availableAcls} availableAcls={availableAcls}
editions={selectedEditions} editions={selectedEditions}
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
className="text-center"/> className="text-center ascribe-button-list collapse-group"/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,13 +1,12 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router';
import Input from 'react-bootstrap/lib/Input'; import Input from 'react-bootstrap/lib/Input';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import Button from 'react-bootstrap/lib/Button'; import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
import Row from 'react-bootstrap/lib/Row';
let Link = Router.Link; import Col from 'react-bootstrap/lib/Col';
let PieceListToolbar = React.createClass({ let PieceListToolbar = React.createClass({
@ -29,15 +28,19 @@ let PieceListToolbar = React.createClass({
<div className="row"> <div className="row">
<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">
<div className="row"> <div className="row">
<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="col-xs-12 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 clear-paddings">
<div className="form-inline"> <Input wrapperClassName='wrapper'>
<Input type='text' ref="search" placeholder="Search..." onChange={this.searchFor} addonAfter={searchIcon} /> <Row>
&nbsp;&nbsp; <Col xs={7} sm={4}>
{/*<PieceListToolbarFilterWidgetFilter />*/} <Input type='text' ref="search" placeholder="Search..." onChange={this.searchFor} addonAfter={searchIcon} />
<Link to="register_piece"> </Col>
<Button>+ Artwork</Button> <Col xs={5} sm={5}>
</Link> <ButtonLink to="register_piece">
</div> + Artwork
</ButtonLink>
</Col>
</Row>
</Input>
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,7 +2,7 @@
export class ColumnModel { export class ColumnModel {
// ToDo: Add validation for all passed-in parameters // ToDo: Add validation for all passed-in parameters
constructor(transformFn, columnName, displayName, displayType, rowWidth, canBeOrdered, transition) { constructor(transformFn, columnName, displayName, displayType, rowWidth, canBeOrdered, transition, className) {
this.transformFn = transformFn; this.transformFn = transformFn;
this.columnName = columnName; this.columnName = columnName;
this.displayName = displayName; this.displayName = displayName;
@ -10,6 +10,7 @@ export class ColumnModel {
this.rowWidth = rowWidth; this.rowWidth = rowWidth;
this.canBeOrdered = canBeOrdered; this.canBeOrdered = canBeOrdered;
this.transition = transition; this.transition = transition;
this.className = className ? className : '';
} }
} }

View File

@ -23,17 +23,18 @@ let TableHeader = React.createClass({
return ( return (
<thead> <thead>
<tr> <tr>
{this.props.columnList.map((val, i) => { {this.props.columnList.map((column, i) => {
let columnClasses = this.calcColumnClasses(this.props.columnList, i, 12); let columnClasses = this.calcColumnClasses(this.props.columnList, i, 12);
let columnName = this.props.columnList[i].columnName; let columnName = column.columnName;
let canBeOrdered = this.props.columnList[i].canBeOrdered; let canBeOrdered = column.canBeOrdered;
return ( return (
<TableHeaderItem <TableHeaderItem
className={column.className}
key={i} key={i}
columnClasses={columnClasses} columnClasses={columnClasses}
displayName={val.displayName} displayName={column.displayName}
columnName={columnName} columnName={columnName}
canBeOrdered={canBeOrdered} canBeOrdered={canBeOrdered}
orderAsc={this.props.orderAsc} orderAsc={this.props.orderAsc}

View File

@ -16,7 +16,8 @@ let TableHeaderItem = React.createClass({
canBeOrdered: React.PropTypes.bool, canBeOrdered: React.PropTypes.bool,
changeOrder: React.PropTypes.func, changeOrder: React.PropTypes.func,
orderAsc: React.PropTypes.bool, orderAsc: React.PropTypes.bool,
orderBy: React.PropTypes.string orderBy: React.PropTypes.string,
className: React.PropTypes.string
}, },
changeOrder() { changeOrder() {
@ -28,7 +29,7 @@ let TableHeaderItem = React.createClass({
if(this.props.columnName === this.props.orderBy) { if(this.props.columnName === this.props.orderBy) {
return ( return (
<th <th
className={'ascribe-table-header-column'} className={'ascribe-table-header-column ' + this.props.className}
onClick={this.changeOrder}> onClick={this.changeOrder}>
<span>{this.props.displayName} <TableHeaderItemCarret orderAsc={this.props.orderAsc} /></span> <span>{this.props.displayName} <TableHeaderItemCarret orderAsc={this.props.orderAsc} /></span>
</th> </th>
@ -36,7 +37,7 @@ let TableHeaderItem = React.createClass({
} else { } else {
return ( return (
<th <th
className={'ascribe-table-header-column'} className={'ascribe-table-header-column ' + this.props.className}
onClick={this.changeOrder}> onClick={this.changeOrder}>
<span>{this.props.displayName}</span> <span>{this.props.displayName}</span>
</th> </th>
@ -44,7 +45,7 @@ let TableHeaderItem = React.createClass({
} }
} else { } else {
return ( return (
<th className={'ascribe-table-header-column'}> <th className={'ascribe-table-header-column ' + this.props.className}>
<span> <span>
{this.props.displayName} {this.props.displayName}
</span> </span>

View File

@ -41,7 +41,7 @@ let TableItemWrapper = React.createClass({
* programmatically * programmatically
*/ */
return ( return (
<td key={i}> <td key={i} className={column.className}>
<Link <Link
className={'ascribe-table-item-column'} className={'ascribe-table-item-column'}
onClick={column.transition.callback} onClick={column.transition.callback}

View File

@ -133,7 +133,7 @@ let FileDragAndDrop = React.createClass({
render: function () { render: function () {
// has files only is true if there are files that do not have the status deleted or canceled // has files only is true if there are files that do not have the status deleted or canceled
let hasFiles = this.props.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0; let hasFiles = this.props.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0;
let className = hasFiles ? 'has-files ' : ''; let className = hasFiles ? 'has-files ' : '';
className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone';
className += this.props.className ? ' ' + this.props.className : ''; className += this.props.className ? ' ' + this.props.className : '';

View File

@ -57,7 +57,7 @@ let FileDragAndDropPreview = React.createClass({
url={this.props.file.url} url={this.props.file.url}
toggleUploadProcess={this.toggleUploadProcess} toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={this.props.areAssetsDownloadable} areAssetsDownloadable={this.props.areAssetsDownloadable}
downloadFile={this.handleDownloadFile}/>); downloadUrl={this.props.file.s3UrlSafe}/>);
} else { } else {
previewElement = (<FileDragAndDropPreviewOther previewElement = (<FileDragAndDropPreviewOther
onClick={this.handleDeleteFile} onClick={this.handleDeleteFile}
@ -65,7 +65,7 @@ let FileDragAndDropPreview = React.createClass({
type={this.props.file.type.split('/')[1]} type={this.props.file.type.split('/')[1]}
toggleUploadProcess={this.toggleUploadProcess} toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={this.props.areAssetsDownloadable} areAssetsDownloadable={this.props.areAssetsDownloadable}
downloadFile={this.handleDownloadFile}/>); downloadUrl={this.props.file.s3UrlSafe}/>);
} }
return ( return (

View File

@ -10,7 +10,7 @@ let FileDragAndDropPreviewImage = React.createClass({
progress: React.PropTypes.number, progress: React.PropTypes.number,
url: React.PropTypes.string, url: React.PropTypes.string,
toggleUploadProcess: React.PropTypes.func, toggleUploadProcess: React.PropTypes.func,
downloadFile: React.PropTypes.func, downloadUrl: React.PropTypes.string,
areAssetsDownloadable: React.PropTypes.bool areAssetsDownloadable: React.PropTypes.bool
}, },
@ -48,7 +48,7 @@ let FileDragAndDropPreviewImage = React.createClass({
// only if assets are actually downloadable, there should be a download icon if the process is already at // only if assets are actually downloadable, there should be a download icon if the process is already at
// 100%. If not, no actionSymbol should be displayed // 100%. If not, no actionSymbol should be displayed
if(this.props.areAssetsDownloadable) { if(this.props.areAssetsDownloadable) {
actionSymbol = <span className="glyphicon glyphicon-download action-file" aria-hidden="true" title="Download file" onClick={this.props.downloadFile}/>; actionSymbol = <a href={this.props.downloadUrl} target="_blank" className="glyphicon glyphicon-download action-file" aria-hidden="true" title="Download file"/>;
} }
} else { } else {

View File

@ -19,7 +19,7 @@ let FileDragAndDropPreviewIterator = React.createClass({
return ( return (
<div> <div>
{this.props.files.map((file, i) => { {this.props.files.map((file, i) => {
if(file.status !== 'deleted' && file.status !== 'canceled') { if(file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1) {
return ( return (
<FileDragAndDropPreview <FileDragAndDropPreview
key={i} key={i}

View File

@ -11,7 +11,7 @@ let FileDragAndDropPreviewOther = React.createClass({
progress: React.PropTypes.number, progress: React.PropTypes.number,
areAssetsDownloadable: React.PropTypes.bool, areAssetsDownloadable: React.PropTypes.bool,
toggleUploadProcess: React.PropTypes.func, toggleUploadProcess: React.PropTypes.func,
downloadFile: React.PropTypes.func downloadUrl: React.PropTypes.string
}, },
getInitialState() { getInitialState() {
@ -44,7 +44,7 @@ let FileDragAndDropPreviewOther = React.createClass({
// only if assets are actually downloadable, there should be a download icon if the process is already at // only if assets are actually downloadable, there should be a download icon if the process is already at
// 100%. If not, no actionSymbol should be displayed // 100%. If not, no actionSymbol should be displayed
if(this.props.areAssetsDownloadable) { if(this.props.areAssetsDownloadable) {
actionSymbol = <span className="glyphicon glyphicon-download action-file" aria-hidden="true" title="Download file" onClick={this.props.downloadFile}/>; actionSymbol = <a href={this.props.downloadUrl} target="_blank" className="glyphicon glyphicon-download action-file" aria-hidden="true" title="Download file"/>;
} }
} else { } else {

View File

@ -276,6 +276,11 @@ var ReactS3FineUploader = React.createClass({
return res.json(); return res.json();
}) })
.then((res) =>{ .then((res) =>{
if(res.otherdata) {
file.s3Url = res.otherdata.url_safe;
} else {
throw new Error('Could not find a url to download.');
}
defer.success(res.key); defer.success(res.key);
}) })
.catch((err) => { .catch((err) => {
@ -327,7 +332,7 @@ var ReactS3FineUploader = React.createClass({
if(success) { if(success) {
// fetch blobs for images // fetch blobs for images
response = response.map((file) => { response = response.map((file) => {
file.url = file.s3Url; file.url = file.s3UrlSafe;
file.status = 'online'; file.status = 'online';
file.progress = 100; file.progress = 100;
return file; return file;
@ -345,6 +350,7 @@ var ReactS3FineUploader = React.createClass({
let newState = React.addons.update(this.state, {filesToUpload: {$set: updatedFilesToUpload}}); let newState = React.addons.update(this.state, {filesToUpload: {$set: updatedFilesToUpload}});
this.setState(newState); this.setState(newState);
} else { } else {
// server has to respond with 204
//let notification = new GlobalNotificationModel('Could not load attached files (Further data)', 'danger', 10000); //let notification = new GlobalNotificationModel('Could not load attached files (Further data)', 'danger', 10000);
//GlobalNotificationActions.appendGlobalNotification(notification); //GlobalNotificationActions.appendGlobalNotification(notification);
// //
@ -388,6 +394,8 @@ var ReactS3FineUploader = React.createClass({
// promise // promise
} else { } else {
let fileToDelete = this.state.filesToUpload[fileId]; let fileToDelete = this.state.filesToUpload[fileId];
fileToDelete.status = 'deleted';
S3Fetcher S3Fetcher
.deleteFile(fileToDelete.s3Key, fileToDelete.s3Bucket) .deleteFile(fileToDelete.s3Key, fileToDelete.s3Bucket)
.then(() => this.onDeleteComplete(fileToDelete.id, null, false)) .then(() => this.onDeleteComplete(fileToDelete.id, null, false))
@ -417,7 +425,6 @@ var ReactS3FineUploader = React.createClass({
}, },
handleUploadFile(files) { handleUploadFile(files) {
// If multiple set and user already uploaded its work, // If multiple set and user already uploaded its work,
// cancel upload // cancel upload
if(!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0) { if(!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0) {
@ -512,7 +519,7 @@ var ReactS3FineUploader = React.createClass({
handleResumeFile={this.handleResumeFile} handleResumeFile={this.handleResumeFile}
multiple={this.props.multiple} multiple={this.props.multiple}
areAssetsDownloadable={this.props.areAssetsDownloadable} areAssetsDownloadable={this.props.areAssetsDownloadable}
dropzoneInactive={!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0} /> dropzoneInactive={!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0} />
</div> </div>
); );
} }

View File

@ -255,7 +255,7 @@ let EditionSummary = React.createClass({
<Row> <Row>
<Col md={12}> <Col md={12}>
<AclButtonList <AclButtonList
className="text-center" className="text-center ascribe-button-list"
availableAcls={this.props.edition.acl} availableAcls={this.props.edition.acl}
editions={[this.props.edition]} editions={[this.props.edition]}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
@ -589,14 +589,14 @@ let CoaDetails = React.createClass({
if (this.state.coa.url_safe) { if (this.state.coa.url_safe) {
return ( return (
<div> <div>
<p className="text-center"> <p className="text-center ascribe-button-list">
<Button bsSize="xsmall" href={this.state.coa.url_safe} target="_blank"> <button className="btn btn-default btn-xs" href={this.state.coa.url_safe} target="_blank">
Download <Glyphicon glyph="cloud-download"/> Download <Glyphicon glyph="cloud-download"/>
</Button> </button>
<Link to="coa_verify"> <Link to="coa_verify">
<Button bsSize="xsmall"> <button className="btn btn-default btn-xs">
Verify <Glyphicon glyph="check"/> Verify <Glyphicon glyph="check"/>
</Button> </button>
</Link> </Link>
</p> </p>

View File

@ -2,7 +2,6 @@
import React from 'react'; import React from 'react';
import { getCookie } from '../utils/fetch_api_utils';
import AppConstants from '../constants/application_constants'; import AppConstants from '../constants/application_constants';
import Router from 'react-router'; import Router from 'react-router';

View File

@ -2,7 +2,7 @@
const languages = { const languages = {
'en-US': { 'en-US': {
'Bitcoin Address': 'Bitcoin Address', 'ID': 'ID',
'Actions': 'Actions', 'Actions': 'Actions',
'Hide': 'Hide', 'Hide': 'Hide',
'Show the edition': 'Show the edition', 'Show the edition': 'Show the edition',
@ -16,7 +16,7 @@ const languages = {
'Next': 'Next' 'Next': 'Next'
}, },
'de': { 'de': {
'Bitcoin Address': 'Bitcoin Adresse', 'ID': 'ID',
'Actions': 'Aktionen', 'Actions': 'Aktionen',
'Hide': 'Verstecke', 'Hide': 'Verstecke',
'Show the edition': 'Zeige die Edition', 'Show the edition': 'Zeige die Edition',

View File

@ -27,6 +27,17 @@ class Requests {
return response.text(); return response.text();
} }
customJSONparse(responseText) {
// If the responses' body does not contain any data,
// fetch will resolve responseText to the string 'None'.
// If this is the case, we can not try to parse it as JSON.
if(responseText !== 'None') {
return JSON.parse(responseText);
} else {
return {};
}
}
handleFatalError(err) { handleFatalError(err) {
this.fatalErrorHandler(err); this.fatalErrorHandler(err);
throw new ServerError(err); throw new ServerError(err);
@ -36,6 +47,7 @@ class Requests {
if (!json.success) { if (!json.success) {
let error = new APIError(); let error = new APIError();
error.json = json; error.json = json;
console.error(new Error('The \'success\' property is missing in the server\'s response.'));
throw error; throw error;
} }
return json; return json;
@ -83,7 +95,7 @@ class Requests {
merged.method = verb; merged.method = verb;
return fetch(url, merged) return fetch(url, merged)
.then(this.unpackResponse) .then(this.unpackResponse)
.then(JSON.parse) .then(this.customJSONparse)
.catch(this.handleFatalError.bind(this)) .catch(this.handleFatalError.bind(this))
.then(this.handleAPIError); .then(this.handleAPIError);
} }

View File

@ -1,8 +1,9 @@
.ascribe-piece-list-bulk-modal { .ascribe-piece-list-bulk-modal {
position: fixed; position: fixed;
top:0; top:0;
width:1170px; left: 3%;
height:6em; width:94%;
background-color: #FAFAFA; background-color: #FAFAFA;
border-left: 0.1em solid #E0E0E0; border-left: 0.1em solid #E0E0E0;
@ -12,6 +13,15 @@
border-bottom-right-radius: 5px; border-bottom-right-radius: 5px;
border-bottom: 0.2em solid #E0E0E0; border-bottom: 0.2em solid #E0E0E0;
z-index:1000; z-index:1000;
padding-bottom: 1em;
}
@media(min-width:1174px){
.ascribe-piece-list-bulk-modal {
left: auto;
max-width: 1174px;
}
} }
.piece-list-bulk-modal-clear-all { .piece-list-bulk-modal-clear-all {

View File

@ -97,6 +97,10 @@
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black; text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
cursor: pointer; cursor: pointer;
&:link, &:visited, &:hover, &:active {
text-decoration: none;
}
&:hover { &:hover {
color: #d9534f; color: #d9534f;
} }
@ -111,6 +115,10 @@
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black; text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
cursor: pointer; cursor: pointer;
&:link, &:visited, &:hover, &:active {
text-decoration: none;
}
&:hover { &:hover {
color: #d9534f; color: #d9534f;
} }

View File

@ -245,3 +245,8 @@ body {
.col-bottom { .col-bottom {
vertical-align: bottom; vertical-align: bottom;
} }
.ascribe-button-list button {
margin-right: 1px;
margin-top: 1px;
}