Add icons for torrent overview details

This commit is contained in:
John Furrow
2016-03-26 23:20:45 -07:00
parent bd38c66428
commit 56ce8888f6
14 changed files with 277 additions and 23 deletions

View File

@@ -82,23 +82,33 @@ $progress-bar--fill--error: #e95779;
position: relative; position: relative;
&:after { &:after {
background: $progress-bar--background; background: $progress-bar--fill;
content: ''; content: '';
height: 1px; height: 1px;
left: 0; left: 0;
opacity: 0.5;
position: absolute; position: absolute;
z-index: 0; z-index: 0;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
transition: background 0.25s; transition: background 0.25s, opacity 0.25s;
width: 100%; width: 100%;
.is-stopped & {
background: $progress-bar--fill--stopped;
}
.has-error & {
background: $progress-bar--fill--error;
}
.is-selected & { .is-selected & {
background: $progress-bar--background--selected; background: $progress-bar--background--selected;
} }
.is-selected.is-stopped & { .is-selected.is-stopped & {
background: $progress-bar--background--selected--stopped; background: $progress-bar--background--selected--stopped;
opacity: 1;
} }
} }
} }

View File

@@ -2,6 +2,7 @@ $torrent-list--background: #fff;
$torrent-list--border: rgba($background, 0.15); $torrent-list--border: rgba($background, 0.15);
$torrent--primary--foreground: #5b6d7c; $torrent--primary--foreground: #5b6d7c;
$torrent--primary--foreground--error: #e95779;
$torrent--primary--foreground--stopped: rgba(#333332, 0.5); $torrent--primary--foreground--stopped: rgba(#333332, 0.5);
$torrent--primary--foreground--selected: #fff; $torrent--primary--foreground--selected: #fff;
$torrent--primary--foreground--selected--stopped: rgba($torrent--primary--foreground--selected, 0.6); $torrent--primary--foreground--selected--stopped: rgba($torrent--primary--foreground--selected, 0.6);
@@ -149,6 +150,10 @@ $torrent--background--error: #e95779;
color: $torrent--primary--foreground--stopped; color: $torrent--primary--foreground--stopped;
} }
.has-error & {
color: $torrent--primary--foreground--error;
}
.is-selected & { .is-selected & {
color: $torrent--primary--foreground--selected; color: $torrent--primary--foreground--selected;
} }
@@ -161,16 +166,26 @@ $torrent--background--error: #e95779;
&--secondary { &--secondary {
align-items: flex-end; align-items: flex-end;
color: $torrent--secondary--foreground; color: $torrent--secondary--foreground;
flex: 1; flex: 0 0 245px;
font-size: 0.75em; font-size: 0.75em;
min-width: 200px; min-width: 255px;
text-align: right;
white-space: nowrap;
vertical-align: baseline;
li { li {
flex: 1 1 auto; display: inline-block;
min-width: 15%; font-weight: 500;
margin-right: 5px;
text-align: left;
white-space: nowrap;
&.torrent__details--ratio { .unit {
max-width: 30px; font-weight: 400;
}
&:last-child {
margin-right: 0;
} }
} }
@@ -195,7 +210,8 @@ $torrent--background--error: #e95779;
li { li {
display: inline-block; display: inline-block;
margin-right: 1em; margin-right: 15px;
white-space: nowrap;
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
@@ -219,6 +235,74 @@ $torrent--background--error: #e95779;
margin-right: 0.5em; margin-right: 0.5em;
opacity: 0.5; opacity: 0.5;
} }
&__icon {
display: inline-block;
padding-right: 5px;
transform: translateY(1px);
vertical-align: baseline;
.icon {
display: block;
fill: currentColor;
height: 12px;
opacity: 0.66;
width: 12px;
}
}
&--completed {
width: 100px;
}
&--uploaded {
width: 60px;
}
&--size {
width: 60px;
}
&--peers {
width: 60px;
}
&--seeds {
width: 60px;
}
&--added {
width: 90px;
}
&--eta {
width: 70px;
}
&--speed {
width: 60px;
&--download,
&--upload {
transition: color 0.25s;
.is-selected & {
color: #fff;
}
}
&--download {
color: $green;
}
&--upload {
color: $blue;
}
}
&--ratio {
width: 50px;
}
} }
} }

View File

@@ -0,0 +1,14 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class CalendarIcon extends BaseIcon {
render() {
return (
<svg className={`icon icon--calendar ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<path d="M51.9,9.39h-4V6.3a5.08,5.08,0,0,0-5-5.15h-2A5.08,5.08,0,0,0,36,6.3V9.39H24V6.3a5.08,5.08,0,0,0-5-5.15h-2a5.08,5.08,0,0,0-5,5.15V9.39h-4a4.08,4.08,0,0,0-4,4.12V54.74a4.08,4.08,0,0,0,4,4.12H51.9a4.08,4.08,0,0,0,4-4.12V13.51A4.08,4.08,0,0,0,51.9,9.39ZM40,6.3a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1v9.28a1,1,0,0,1-1,1h-2a1,1,0,0,1-1-1V6.3Zm-23.89,0a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1v9.28a1,1,0,0,1-1,1h-2a1,1,0,0,1-1-1V6.3ZM49.8,52.84H10.2V22.42H49.8V52.84Z"/>
</svg>
);
}
}

View File

@@ -0,0 +1,15 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class ClockIcon extends BaseIcon {
render() {
return (
<svg className={`icon icon--clock ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<path d="M30,6A24,24,0,0,1,47,47,24,24,0,0,1,13,13,23.85,23.85,0,0,1,30,6m0-6A30,30,0,1,0,51.21,8.79,29.91,29.91,0,0,0,30,0h0Z"/>
<polygon points="26.85 46.91 21.18 44.09 28.58 29.21 17.93 22.93 21.14 17.48 36.88 26.75 26.85 46.91"/>
</svg>
);
}
}

View File

@@ -0,0 +1,24 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class DiskIcon extends BaseIcon {
render() {
return (
<svg className={`icon icon--disk ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<polygon points="51.8,0 51.8,0 51.8,3.7 51.8,56.3 8.2,56.3 8.2,3.7 8.2,0 8.2,0 4.5,0 4.5,60.1 55.5,60.1 55.5,0 "/>
<rect x="18.4" y="8.9" width="23.1" height="3.8"/>
<path d="M30,51.8c3.4,0,6.8-1.1,9.6-3.3L29.4,38.4l3.1-3.1l10.2,10.1c4.3-6.1,3.8-14.5-1.7-20c-3-3-7-4.5-11-4.5
s-7.9,1.5-11,4.5C13,31.4,13,41.2,19,47.3C22.1,50.3,26,51.8,30,51.8z"/>
<rect x="40" y="0" width="3.5" height="3.8"/>
<rect x="45.9" y="0" width="3.5" height="3.8"/>
<rect x="34.1" y="0" width="3.5" height="3.8"/>
<rect x="16.5" y="0" width="3.5" height="3.8"/>
<rect x="10.6" y="0" width="3.5" height="3.8"/>
<rect x="22.4" y="0" width="3.5" height="3.8"/>
<rect x="28.2" y="0" width="3.5" height="3.8"/>
</svg>
);
}
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class DownloadThickIcon extends BaseIcon {
render() {
return (
<svg className={`icon icon--download ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<polygon points="44.1,23 33,39.7 33,4.6 27,4.6 27,39.7 15.9,23 10.9,26.4 30,55 49.1,26.4 "/>
</svg>
);
}
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class PeersIcon extends BaseIcon {
render() {
return (
<svg className={`icon icon--peers ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<path d="M47.95,1.4a12.05,12.05,0,0,0-11.78,9.55H23.84A12,12,0,1,0,12.05,25.5a11.92,11.92,0,0,0,3.56-.6l6,13a12.51,12.51,0,1,0,4.28-2.66L20,22.45a12,12,0,0,0,3.85-6.5H36.16A12,12,0,1,0,47.95,1.4ZM37.05,46.55A7.05,7.05,0,1,1,30,39.5,7.06,7.06,0,0,1,37.05,46.55ZM5,13.45a7.05,7.05,0,1,1,7.05,7.05A7.06,7.06,0,0,1,5,13.45ZM47.95,20.5A7.05,7.05,0,1,1,55,13.45,7.06,7.06,0,0,1,47.95,20.5Z"/>
</svg>
);
}
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class RatioIcon extends BaseIcon {
render() {
return (
<svg className={`icon icon--ratio ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<path d="M60,4.85L55.14,0l-7,6.94a29.17,29.17,0,0,0-5.24-3.3l-3,6.16a22.39,22.39,0,0,1,3.37,2L12,43c-0.31-.43-0.62-0.86-0.9-1.31L5.27,45.35a29.07,29.07,0,0,0,1.81,2.55l-7,6.93L5,59.68l7-6.94a29.32,29.32,0,0,0,38.81-2.28A29.08,29.08,0,0,0,53,11.78ZM45.89,45.61a22.39,22.39,0,0,1-29,2.22L48.12,16.69A22.2,22.2,0,0,1,45.89,45.61ZM32.7,7.68L33.5,0.87a29.72,29.72,0,0,0-6.57,0l0.73,6.81A22.35,22.35,0,0,1,32.7,7.68Zm-12.26,2-3-6.19A29.23,29.23,0,0,0,11.9,7l4.27,5.37A22.31,22.31,0,0,1,20.44,9.69ZM8.23,34.78a22.48,22.48,0,0,1-.55-5l-6.88,0a29.31,29.31,0,0,0,.72,6.45Zm2.91-16.87L5.33,14.24a29,29,0,0,0-2.87,5.89l6.49,2.28A22.15,22.15,0,0,1,11.14,17.91Z"/>
</svg>
);
}
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class SeedsIcon extends BaseIcon {
render() {
return (
<svg className={`icon icon--seeds ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<path d="M47.95,1.4a12.05,12.05,0,0,0-11.78,9.55H23.84A12,12,0,1,0,12.05,25.5a11.92,11.92,0,0,0,3.56-.6l6,13a12.51,12.51,0,1,0,4.28-2.66L20,22.45a12,12,0,0,0,3.85-6.5H36.16A12,12,0,1,0,47.95,1.4ZM37.05,46.55A7.05,7.05,0,1,1,30,39.5,7.06,7.06,0,0,1,37.05,46.55Z"/>
</svg>
);
}
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import BaseIcon from './BaseIcon';
export default class UploadThickIcon extends BaseIcon {
render() {
return (
<svg className={`icon icon--upload ${this.props.className}`}
xmlns={this.getXmlns()} viewBox={this.getViewBox()}>
<polygon points="15.9,36.6 27,19.9 27,55 33,55 33,19.9 44.1,36.6 49.1,33.3 30,4.6 10.9,33.3 "/>
</svg>
);
}
}

View File

@@ -1,12 +1,20 @@
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react'; import React from 'react';
import CalendarIcon from '../icons/CalendarIcon';
import ClockIcon from '../icons/ClockIcon';
import DiskIcon from '../icons/DiskIcon';
import DotsMini from '../icons/DotsMini'; import DotsMini from '../icons/DotsMini';
import DownloadThickIcon from '../icons/DownloadThickIcon';
import EventTypes from '../../constants/EventTypes'; import EventTypes from '../../constants/EventTypes';
import format from '../../util/formatData'; import format from '../../util/formatData';
import PeersIcon from '../icons/PeersIcon';
import ProgressBar from '../ui/ProgressBar'; import ProgressBar from '../ui/ProgressBar';
import RatioIcon from '../icons/RatioIcon';
import SeedsIcon from '../icons/SeedsIcon';
import {torrentStatusIcons} from '../../util/torrentStatusIcons'; import {torrentStatusIcons} from '../../util/torrentStatusIcons';
import {torrentStatusClasses} from '../../util/torrentStatusClasses'; import {torrentStatusClasses} from '../../util/torrentStatusClasses';
import UploadThickIcon from '../icons/UploadThickIcon';
const METHODS_TO_BIND = [ const METHODS_TO_BIND = [
'handleClick', 'handleClick',
@@ -61,17 +69,21 @@ export default class Torrent extends React.Component {
<li className="torrent__details--secondary"> <li className="torrent__details--secondary">
<ul className="torrent__details"> <ul className="torrent__details">
<li className="torrent__details--eta"> <li className="torrent__details--eta">
<span className="torrent__details__icon"><ClockIcon /></span>
{eta} {eta}
</li> </li>
<li className="torrent__details--speed"> <li className="torrent__details--speed torrent__details--speed--download">
<span className="torrent__details__icon"><DownloadThickIcon /></span>
{downloadRate.value} {downloadRate.value}
<em className="unit">{downloadRate.unit}</em> <em className="unit">{downloadRate.unit}</em>
</li> </li>
<li className="torrent__details--speed"> <li className="torrent__details--speed torrent__details--speed--upload">
<span className="torrent__details__icon"><UploadThickIcon /></span>
{uploadRate.value} {uploadRate.value}
<em className="unit">{uploadRate.unit}</em> <em className="unit">{uploadRate.unit}</em>
</li> </li>
<li className="torrent__details--ratio"> <li className="torrent__details--ratio">
<span className="torrent__details__icon"><RatioIcon /></span>
{ratio} {ratio}
</li> </li>
</ul> </ul>
@@ -79,7 +91,7 @@ export default class Torrent extends React.Component {
</ul> </ul>
<ul className="torrent__details torrent__details--tertiary"> <ul className="torrent__details torrent__details--tertiary">
<li className="torrent__details--completed"> <li className="torrent__details--completed">
<span className="torrent__details__label">Downloaded</span> <span className="torrent__details__icon"><DownloadThickIcon /></span>
{torrent.percentComplete} {torrent.percentComplete}
<em className="unit">%</em> <em className="unit">%</em>
&nbsp;&mdash;&nbsp; &nbsp;&mdash;&nbsp;
@@ -87,17 +99,25 @@ export default class Torrent extends React.Component {
<em className="unit">{completed.unit}</em> <em className="unit">{completed.unit}</em>
</li> </li>
<li className="torrent__details--uploaded"> <li className="torrent__details--uploaded">
<span className="torrent__details__label">Uploaded</span> <span className="torrent__details__icon"><UploadThickIcon /></span>
{uploadTotal.value} {uploadTotal.value}
<em className="unit">{uploadTotal.unit}</em> <em className="unit">{uploadTotal.unit}</em>
</li> </li>
<li className="torrent__details--size"> <li className="torrent__details--size">
<span className="torrent__details__label">Size</span> <span className="torrent__details__icon"><DiskIcon /></span>
{totalSize.value} {totalSize.value}
<em className="unit">{totalSize.unit}</em> <em className="unit">{totalSize.unit}</em>
</li> </li>
<li className="torrent__details--peers">
<span className="torrent__details__icon"><PeersIcon /></span>
{torrent.connectedPeers} <em className="unit">of</em> {torrent.totalPeers}
</li>
<li className="torrent__details--seeds">
<span className="torrent__details__icon"><SeedsIcon /></span>
{torrent.connectedSeeds} <em className="unit">of</em> {torrent.totalSeeds}
</li>
<li className="torrent__details--added"> <li className="torrent__details--added">
<span className="torrent__details__label">Added</span> <span className="torrent__details__icon"><CalendarIcon /></span>
{addedString} {addedString}
</li> </li>
</ul> </ul>

View File

@@ -73,29 +73,38 @@ const format = {
megabyte = kilobyte * 1024, megabyte = kilobyte * 1024,
gigabyte = megabyte * 1024, gigabyte = megabyte * 1024,
terabyte = gigabyte * 1024, terabyte = gigabyte * 1024,
value = '', value = 0,
unit = ''; unit = '';
if ((bytes >= 0) && (bytes < kilobyte)) { if ((bytes >= 0) && (bytes < kilobyte)) {
value = bytes; value = bytes;
unit = 'B'; unit = 'B';
} else if ((bytes >= kilobyte) && (bytes < megabyte)) { } else if ((bytes >= kilobyte) && (bytes < megabyte)) {
value = (bytes / kilobyte).toFixed(precision); value = (bytes / kilobyte);
unit = 'KB'; unit = 'KB';
} else if ((bytes >= megabyte) && (bytes < gigabyte)) { } else if ((bytes >= megabyte) && (bytes < gigabyte)) {
value = (bytes / megabyte).toFixed(precision); value = (bytes / megabyte);
unit = 'MB'; unit = 'MB';
} else if ((bytes >= gigabyte) && (bytes < terabyte)) { } else if ((bytes >= gigabyte) && (bytes < terabyte)) {
value = (bytes / gigabyte).toFixed(precision); value = (bytes / gigabyte);
unit = 'GB'; unit = 'GB';
} else if (bytes >= terabyte) { } else if (bytes >= terabyte) {
value = (bytes / terabyte).toFixed(precision); value = (bytes / terabyte);
unit = 'TB'; unit = 'TB';
} else { } else {
value = bytes; value = bytes;
unit = 'B'; unit = 'B';
} }
value = Number(value);
if (!!value && value < 10) {
value = value.toFixed(precision);
} else if (!!value && value > 10 && value < 100) {
value = value.toFixed(precision - 1);
} else if (!!value && value > 100) {
value = Math.floor(value);
}
if (extraUnits) { if (extraUnits) {
unit += extraUnits; unit += extraUnits;
} }

View File

@@ -145,7 +145,15 @@ class Torrent {
} }
getCalculatedPercentComplete(clientData) { getCalculatedPercentComplete(clientData) {
return (clientData.bytesDone / clientData.sizeBytes * 100).toFixed(2); let percentComplete = clientData.bytesDone / clientData.sizeBytes * 100;
if (percentComplete > 0 && percentComplete < 10) {
return percentComplete.toFixed(2);
} else if (percentComplete > 10 && percentComplete < 100) {
return percentComplete.toFixed(1);
} else {
return percentComplete;
}
} }
getCalculatedStatus(clientData) { getCalculatedStatus(clientData) {