mirror of
https://github.com/zoriya/flood.git
synced 2025-12-20 14:15:15 +00:00
Add tabs in AddTorrent modal
This commit is contained in:
@@ -33,6 +33,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.is-fulfilled {
|
||||
background: $textbox--fulfilled--background;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
@@ -89,14 +92,14 @@
|
||||
&__label {
|
||||
color: $form--label--foreground;
|
||||
display: block;
|
||||
margin-bottom: 0.1em;
|
||||
font-size: 0.8em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
&__row {
|
||||
|
||||
& + .form__row {
|
||||
margin-top: 20px;
|
||||
margin-top: $spacing-unit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
cursor: pointer;
|
||||
height: 16px;
|
||||
outline: none;
|
||||
margin-right: 8px;
|
||||
margin-right: 6px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
transition: background 0.25s, box-shadow 0.25s;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
$modal--border-radius: 3px;
|
||||
$modal-content--padding--horizontal: $spacing-unit;
|
||||
$modal-content--padding--vertical: $modal-content--padding--horizontal * 1/2;
|
||||
|
||||
.modal {
|
||||
background: $modal--overlay;
|
||||
height: 100%;
|
||||
@@ -8,21 +12,78 @@
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
|
||||
&__header {
|
||||
background: $modal--heading--background;
|
||||
border-radius: $modal--border-radius $modal--border-radius 0 0;
|
||||
box-shadow: inset 0 -1px 0 $modal--heading--border;
|
||||
color: $modal--heading--foreground;
|
||||
flex: 0 0 auto;
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
padding: $modal-content--padding--vertical $modal-content--padding--horizontal 0 $modal-content--padding--horizontal;
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
color: $modal--tab--foreground;
|
||||
font-size: 0.7em;
|
||||
margin: 0 $spacing-unit * -1/5;
|
||||
|
||||
.modal {
|
||||
|
||||
&__tab {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-right: $spacing-unit * 1/4;
|
||||
padding: $spacing-unit * 1/5;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
bottom: 0;
|
||||
content: '';
|
||||
height: 1px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
transition: background 0.25s;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: $modal--tab--foreground--active;
|
||||
font-weight: 600;
|
||||
|
||||
&:after {
|
||||
background: $modal--tab--border--active;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
padding: $modal-content--padding--vertical $modal-content--padding--horizontal;
|
||||
|
||||
&__wrapper {
|
||||
background: $modal--background;
|
||||
border-radius: 5px;
|
||||
border-radius: $modal--border-radius;
|
||||
box-shadow:
|
||||
0 0 0 1px $modal--content--border,
|
||||
0 0 35px $modal--content--shadow;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
left: 50%;
|
||||
max-height: 80%;
|
||||
max-width: 80%;
|
||||
overflow: auto;
|
||||
padding: 30px;
|
||||
position: absolute;
|
||||
top: 10%;
|
||||
transform: translate(-50%, 0);
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
&--align-center {
|
||||
text-align: center;
|
||||
@@ -31,6 +92,7 @@
|
||||
|
||||
&__footer {
|
||||
margin-top: $spacing-unit;
|
||||
padding: 0 0 $modal-content--padding--vertical 0;
|
||||
}
|
||||
|
||||
&__button-group {
|
||||
@@ -44,13 +106,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
color: $modal--header--foreground;
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__animation-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@@ -20,4 +20,14 @@
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
|
||||
&__row {
|
||||
|
||||
& + .form__row {
|
||||
margin-top: $spacing-unit * 1/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,15 @@ $main-content--background: #e9eef2;
|
||||
$header--foreground: #313436;
|
||||
|
||||
// form elements
|
||||
$form--label--foreground: #53718a;
|
||||
$form--label--foreground: #abbac7;
|
||||
|
||||
$textbox--background: rgba($main-content--background, 0.3);
|
||||
$textbox--foreground: $foreground;
|
||||
$textbox--background: #e9eff5;
|
||||
$textbox--foreground: #53718a;
|
||||
$textbox--placeholder: #abbac7;
|
||||
$textbox--border: $main-content--background;
|
||||
$textbox--active--background: $textbox--background;
|
||||
$textbox--active--border: $main-content--background;
|
||||
$textbox--border: #d6e2ea;
|
||||
$textbox--fulfilled--background: #fdfefe;
|
||||
$textbox--active--background: #fdfefe;
|
||||
$textbox--active--border: desaturate(darken($textbox--border, 5%), 5%);
|
||||
$textbox--active--foreground: $blue;
|
||||
$textbox--active--placeholder: $textbox--placeholder;
|
||||
|
||||
@@ -138,7 +139,7 @@ $torrent-details--navigation--border: rgba(#040d13, 0.4);
|
||||
$torrent-details--navigation--item--background--active: rgba(#349cf4, 0.07);
|
||||
$torrent-details--navigation--item--foreground--active: #349cf4;
|
||||
$torrent-details--navigation--item--border--active: #349cf4;
|
||||
$torrent-details--navigation--background: transparent; // rgba(desaturate(#0c1b26, 15%), 0.1)
|
||||
$torrent-details--navigation--background: transparent;
|
||||
|
||||
$torrent-details--content--background: rgba(desaturate(#0c1b26, 15%), 0.4);
|
||||
|
||||
@@ -173,9 +174,14 @@ $dropdown--item--foreground--hover: darken($dropdown--foreground, 10%);
|
||||
$dropdown--item--foreground--active: $blue;
|
||||
|
||||
// modal windows
|
||||
$modal--background: #fff;
|
||||
$modal--background: #f7fafc;
|
||||
$modal--heading--background: #fff;
|
||||
$modal--heading--foreground: $header--foreground;
|
||||
$modal--heading--border: #dde7ed;
|
||||
$modal--tab--foreground: #abbac7;
|
||||
$modal--tab--foreground--active: $blue;
|
||||
$modal--tab--border--active: $blue;
|
||||
$modal--overlay: rgba($background, 0.5);
|
||||
$modal--header--foreground: $header--foreground;
|
||||
$modal--content--border: rgba($background, 0.1);
|
||||
$modal--content--shadow: rgba($background, 0.3);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ const METHODS_TO_BIND = [
|
||||
];
|
||||
|
||||
export default class TextboxRepeater extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -38,9 +37,13 @@ export default class TextboxRepeater extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let inputClasses = classnames('textbox', {
|
||||
'is-fulfilled': textbox.value && textbox.value !== ''
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="textbox__wrapper form__row" key={index}>
|
||||
<input className="textbox"
|
||||
<input className={inputClasses}
|
||||
onChange={this.handleTextboxChange.bind(textbox, index)}
|
||||
placeholder={this.props.placeholder}
|
||||
value={textbox.value}
|
||||
@@ -66,5 +69,4 @@ export default class TextboxRepeater extends React.Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import _ from 'lodash';
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import AddTorrentsByFile from './AddTorrentsByFile';
|
||||
import AddTorrentsByURL from './AddTorrentsByURL';
|
||||
import EventTypes from '../../constants/EventTypes';
|
||||
import LoadingIndicatorDots from '../icons/LoadingIndicatorDots';
|
||||
import Modal from './Modal';
|
||||
@@ -11,16 +13,7 @@ import TorrentStore from '../../stores/TorrentStore';
|
||||
import UIActions from '../../actions/UIActions';
|
||||
import UIStore from '../../stores/UIStore';
|
||||
|
||||
const METHODS_TO_BIND = [
|
||||
'getContent',
|
||||
'handleDestinationChange',
|
||||
'handleUrlAdd',
|
||||
'handleUrlChange',
|
||||
'handleUrlRemove',
|
||||
'handleAddTorrents',
|
||||
'onAddTorrentSuccess',
|
||||
'onLatestTorrentLocationChange'
|
||||
];
|
||||
const METHODS_TO_BIND = ['onAddTorrentSuccess'];
|
||||
|
||||
export default class AddTorrents extends React.Component {
|
||||
constructor() {
|
||||
@@ -39,18 +32,12 @@ export default class AddTorrents extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({destination: UIStore.getLatestTorrentLocation()});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
TorrentStore.listen(EventTypes.CLIENT_ADD_TORRENT_SUCCESS, this.onAddTorrentSuccess);
|
||||
UIStore.listen(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
TorrentStore.unlisten(EventTypes.CLIENT_ADD_TORRENT_SUCCESS, this.onAddTorrentSuccess);
|
||||
UIStore.unlisten(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
|
||||
UIStore.fetchLatestTorrentLocation();
|
||||
}
|
||||
|
||||
@@ -69,10 +56,6 @@ export default class AddTorrents extends React.Component {
|
||||
this.dismissModal();
|
||||
}
|
||||
|
||||
onLatestTorrentLocationChange() {
|
||||
this.setState({destination: UIStore.getLatestTorrentLocation()});
|
||||
}
|
||||
|
||||
getActions() {
|
||||
let icon = null;
|
||||
let primaryButtonText = 'Add Torrent';
|
||||
@@ -104,80 +87,27 @@ export default class AddTorrents extends React.Component {
|
||||
];
|
||||
}
|
||||
|
||||
getContent() {
|
||||
let error = null;
|
||||
|
||||
if (this.state.addTorrentsError) {
|
||||
error = (
|
||||
<div className="form__row">
|
||||
{this.state.addTorrentsError}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form">
|
||||
{error}
|
||||
<div className="form__row">
|
||||
<TextboxRepeater placeholder="Torrent URL"
|
||||
handleTextboxAdd={this.handleUrlAdd}
|
||||
handleTextboxChange={this.handleUrlChange}
|
||||
handleTextboxRemove={this.handleUrlRemove}
|
||||
textboxes={this.state.urlTextboxes} />
|
||||
</div>
|
||||
<div className="form__row">
|
||||
<input className="textbox"
|
||||
onChange={this.handleDestinationChange}
|
||||
placeholder="Destination"
|
||||
value={this.state.destination}
|
||||
type="text" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
handleAddTorrents() {
|
||||
this.setState({isAddingTorrents: true});
|
||||
let torrentUrls = _.map(this.state.urlTextboxes, 'value');
|
||||
TorrentActions.addTorrents(torrentUrls, this.state.destination);
|
||||
}
|
||||
|
||||
handleDestinationChange(event) {
|
||||
this.setState({
|
||||
destination: event.target.value
|
||||
})
|
||||
}
|
||||
|
||||
handleMenuWrapperClick(event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
handleUrlRemove(index) {
|
||||
let urlTextboxes = Object.assign([], this.state.urlTextboxes);
|
||||
urlTextboxes.splice(index, 1);
|
||||
this.setState({
|
||||
urlTextboxes
|
||||
});
|
||||
}
|
||||
|
||||
handleUrlAdd(index) {
|
||||
let urlTextboxes = Object.assign([], this.state.urlTextboxes);
|
||||
urlTextboxes.splice(index + 1, 0, {value: null});
|
||||
this.setState({urlTextboxes});
|
||||
}
|
||||
|
||||
handleUrlChange(index, value) {
|
||||
let urlTextboxes = Object.assign([], this.state.urlTextboxes);
|
||||
urlTextboxes[index].value = value;
|
||||
this.setState({urlTextboxes});
|
||||
getAddByFileContent() {
|
||||
return <span>add by file</span>;
|
||||
}
|
||||
|
||||
render() {
|
||||
let tabs = {
|
||||
'by-url': {
|
||||
content: <AddTorrentsByURL />,
|
||||
label: 'By URL'
|
||||
},
|
||||
'by-file': {
|
||||
content: <AddTorrentsByFile />,
|
||||
label: 'By File'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal heading="Add Torrents"
|
||||
content={this.getContent()}
|
||||
actions={this.getActions()}
|
||||
dismiss={this.dismissModal} />
|
||||
dismiss={this.dismissModal}
|
||||
tabs={tabs} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
35
client/source/scripts/components/modals/AddTorrentsByFile.js
Normal file
35
client/source/scripts/components/modals/AddTorrentsByFile.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import Dropzone from 'react-dropzone';
|
||||
import React from 'react';
|
||||
|
||||
const METHODS_TO_BIND = ['handleOpenClick'];
|
||||
|
||||
export default class AddTorrents extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
files: null
|
||||
};
|
||||
|
||||
METHODS_TO_BIND.forEach((method) => {
|
||||
this[method] = this[method].bind(this);
|
||||
});
|
||||
}
|
||||
|
||||
handleFileDrop(files) {
|
||||
this.setState({files});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="form">
|
||||
<Dropzone className="form__dropzone dropzone" ref="dropzone" onDrop={this.handleFileDrop}>
|
||||
Drop some files here,
|
||||
<span className="dropzone__browse-button">
|
||||
or click to browse.
|
||||
</span>
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
132
client/source/scripts/components/modals/AddTorrentsByURL.js
Normal file
132
client/source/scripts/components/modals/AddTorrentsByURL.js
Normal file
@@ -0,0 +1,132 @@
|
||||
import _ from 'lodash';
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import EventTypes from '../../constants/EventTypes';
|
||||
import LoadingIndicatorDots from '../icons/LoadingIndicatorDots';
|
||||
import Modal from './Modal';
|
||||
import TextboxRepeater from '../forms/TextboxRepeater';
|
||||
import TorrentActions from '../../actions/TorrentActions';
|
||||
import TorrentStore from '../../stores/TorrentStore';
|
||||
import UIActions from '../../actions/UIActions';
|
||||
import UIStore from '../../stores/UIStore';
|
||||
|
||||
const METHODS_TO_BIND = [
|
||||
'handleDestinationChange',
|
||||
'handleUrlAdd',
|
||||
'handleUrlChange',
|
||||
'handleUrlRemove',
|
||||
'handleAddTorrents',
|
||||
'onLatestTorrentLocationChange'
|
||||
];
|
||||
|
||||
export default class AddTorrents extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
addTorrentsError: null,
|
||||
destination: null,
|
||||
isAddingTorrents: false,
|
||||
urlTextboxes: [{value: null}]
|
||||
};
|
||||
|
||||
METHODS_TO_BIND.forEach((method) => {
|
||||
this[method] = this[method].bind(this);
|
||||
});
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({destination: UIStore.getLatestTorrentLocation()});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
UIStore.listen(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
UIStore.unlisten(EventTypes.UI_LATEST_TORRENT_LOCATION_CHANGE, this.onLatestTorrentLocationChange);
|
||||
UIStore.fetchLatestTorrentLocation();
|
||||
}
|
||||
|
||||
dismissModal() {
|
||||
UIActions.dismissModal();
|
||||
}
|
||||
|
||||
onAddTorrentError() {
|
||||
this.setState({
|
||||
addTorrentsError: 'There was an error, but I have no idea what happened!',
|
||||
isAddingTorrents: false
|
||||
});
|
||||
}
|
||||
|
||||
onLatestTorrentLocationChange() {
|
||||
this.setState({destination: UIStore.getLatestTorrentLocation()});
|
||||
}
|
||||
|
||||
handleAddTorrents() {
|
||||
this.setState({isAddingTorrents: true});
|
||||
let torrentUrls = _.map(this.state.urlTextboxes, 'value');
|
||||
TorrentActions.addTorrents(torrentUrls, this.state.destination);
|
||||
}
|
||||
|
||||
handleDestinationChange(event) {
|
||||
this.setState({destination: event.target.value});
|
||||
}
|
||||
|
||||
handleUrlRemove(index) {
|
||||
let urlTextboxes = Object.assign([], this.state.urlTextboxes);
|
||||
urlTextboxes.splice(index, 1);
|
||||
this.setState({urlTextboxes});
|
||||
}
|
||||
|
||||
handleUrlAdd(index) {
|
||||
let urlTextboxes = Object.assign([], this.state.urlTextboxes);
|
||||
urlTextboxes.splice(index + 1, 0, {value: null});
|
||||
this.setState({urlTextboxes});
|
||||
}
|
||||
|
||||
handleUrlChange(index, value) {
|
||||
let urlTextboxes = Object.assign([], this.state.urlTextboxes);
|
||||
urlTextboxes[index].value = value;
|
||||
this.setState({urlTextboxes});
|
||||
}
|
||||
|
||||
render() {
|
||||
let error = null;
|
||||
|
||||
if (this.state.addTorrentsError) {
|
||||
error = (
|
||||
<div className="form__row">
|
||||
{this.state.addTorrentsError}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form">
|
||||
{error}
|
||||
<div className="form__row">
|
||||
<label className="form__label">
|
||||
Torrents
|
||||
</label>
|
||||
<TextboxRepeater placeholder="Torrent URL"
|
||||
handleTextboxAdd={this.handleUrlAdd}
|
||||
handleTextboxChange={this.handleUrlChange}
|
||||
handleTextboxRemove={this.handleUrlRemove}
|
||||
textboxes={this.state.urlTextboxes} />
|
||||
</div>
|
||||
<div className="form__row">
|
||||
<label className="form__label">
|
||||
Destination
|
||||
</label>
|
||||
<input className="textbox"
|
||||
onChange={this.handleDestinationChange}
|
||||
placeholder="Destination"
|
||||
value={this.state.destination}
|
||||
type="text" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,32 @@ import _ from 'lodash';
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import CustomScrollbars from '../ui/CustomScrollbars';
|
||||
import ModalTabs from './ModalTabs';
|
||||
|
||||
const METHODS_TO_BIND = ['handleTabChange'];
|
||||
|
||||
export default class Modal extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
activeTabId: null
|
||||
};
|
||||
|
||||
METHODS_TO_BIND.forEach((method) => {
|
||||
this[method] = this[method].bind(this);
|
||||
});
|
||||
}
|
||||
|
||||
getActiveTabId() {
|
||||
if (this.state.activeTabId) {
|
||||
return this.state.activeTabId;
|
||||
}
|
||||
|
||||
return Object.keys(this.props.tabs)[0];
|
||||
}
|
||||
|
||||
getModalButtons(actions) {
|
||||
let buttons = actions.map((action, index) => {
|
||||
let classes = classnames('button', {
|
||||
@@ -37,24 +62,43 @@ export default class Modal extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleTabChange(tab) {
|
||||
this.setState({activeTabId: tab.id});
|
||||
}
|
||||
|
||||
handleMenuWrapperClick(event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
render() {
|
||||
let contentClasses = classnames('modal__content',
|
||||
`modal__content--align-${this.props.alignment}`);
|
||||
let content = this.props.content;
|
||||
let contentClasses = classnames('modal__content__wrapper',
|
||||
`modal--align-${this.props.alignment}`);
|
||||
let tabs = null;
|
||||
|
||||
if (this.props.tabs) {
|
||||
let activeTabId = this.getActiveTabId();
|
||||
|
||||
content = this.props.tabs[activeTabId].content;
|
||||
tabs = (
|
||||
<ModalTabs activeTabId={activeTabId} onTabChange={this.handleTabChange}
|
||||
tabs={this.props.tabs} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={contentClasses} onClick={this.handleMenuWrapperClick}>
|
||||
<div className="modal__header">{this.props.heading}</div>
|
||||
<div className="modal__content__container">
|
||||
{this.props.content}
|
||||
<div className="modal__header">
|
||||
{this.props.heading}
|
||||
{tabs}
|
||||
</div>
|
||||
<div className="modal__content">
|
||||
{content}
|
||||
<div className="modal__footer">
|
||||
{this.getModalButtons(this.props.actions)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
38
client/source/scripts/components/modals/ModalTabs.js
Normal file
38
client/source/scripts/components/modals/ModalTabs.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
export default class ModalTabs extends React.Component {
|
||||
handleTabClick(tab) {
|
||||
if (this.props.onTabChange) {
|
||||
this.props.onTabChange(tab);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let tabs = Object.keys(this.props.tabs).map((tabId, index) => {
|
||||
let currentTab = this.props.tabs[tabId];
|
||||
|
||||
currentTab.id = tabId;
|
||||
|
||||
let classes = classnames('modal__tab', {
|
||||
'is-active': tabId === this.props.activeTabId
|
||||
});
|
||||
|
||||
return (
|
||||
<li className={classes} key={index}
|
||||
onClick={this.handleTabClick.bind(this, currentTab)}>
|
||||
{currentTab.label}
|
||||
</li>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<ul className="modal__tabs">
|
||||
{tabs}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ModalTabs.defaultProps = {
|
||||
tabs: []
|
||||
};
|
||||
@@ -38,6 +38,7 @@
|
||||
"react-addons-css-transition-group": "^0.14.7",
|
||||
"react-custom-scrollbars": "^3.0.0",
|
||||
"react-dom": "^0.14.7",
|
||||
"react-dropzone": "^3.3.2",
|
||||
"sax": "^0.6.1",
|
||||
"serve-favicon": "~2.2.0",
|
||||
"xmlbuilder": "^2.6.2",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -484,9 +484,9 @@ th {
|
||||
outline: none; }
|
||||
|
||||
.textbox {
|
||||
background: rgba(233, 238, 242, 0.3);
|
||||
background: #e9eff5;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e9eef2;
|
||||
border: 1px solid #d6e2ea;
|
||||
color: #53718a;
|
||||
display: block;
|
||||
font-size: 0.9em;
|
||||
@@ -515,8 +515,8 @@ th {
|
||||
-webkit-transition: color 0.25s;
|
||||
transition: color 0.25s; }
|
||||
.textbox:focus {
|
||||
background: rgba(233, 238, 242, 0.3);
|
||||
border-color: #e9eef2;
|
||||
background: #fdfefe;
|
||||
border-color: #c7d6df;
|
||||
color: #258de5; }
|
||||
.textbox:focus::-webkit-input-placeholder {
|
||||
color: #abbac7; }
|
||||
@@ -526,6 +526,8 @@ th {
|
||||
color: #abbac7; }
|
||||
.textbox:focus::placeholder {
|
||||
color: #abbac7; }
|
||||
.textbox.is-fulfilled {
|
||||
background: #fdfefe; }
|
||||
|
||||
.button {
|
||||
background: transparent;
|
||||
@@ -560,12 +562,13 @@ th {
|
||||
background: #1a80d7; }
|
||||
|
||||
.form__label {
|
||||
color: #53718a;
|
||||
color: #abbac7;
|
||||
display: block;
|
||||
margin-bottom: 0.1em; }
|
||||
font-size: 0.8em;
|
||||
margin-bottom: 0.5em; }
|
||||
|
||||
.form__row + .form__row {
|
||||
margin-top: 20px; }
|
||||
margin-top: 25px; }
|
||||
|
||||
html,
|
||||
body {
|
||||
@@ -1140,7 +1143,7 @@ body {
|
||||
cursor: pointer;
|
||||
height: 16px;
|
||||
outline: none;
|
||||
margin-right: 8px;
|
||||
margin-right: 6px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
-webkit-transition: background 0.25s, box-shadow 0.25s;
|
||||
@@ -1267,15 +1270,67 @@ body {
|
||||
transition: opacity 0.5s;
|
||||
width: 100%;
|
||||
z-index: 100; }
|
||||
.modal__content {
|
||||
.modal__header {
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
border-radius: 3px 3px 0 0;
|
||||
box-shadow: inset 0 -1px 0 #dde7ed;
|
||||
color: #313436;
|
||||
-webkit-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
padding: 12.5px 25px 0 25px; }
|
||||
.modal__tabs {
|
||||
color: #abbac7;
|
||||
font-size: 0.7em;
|
||||
margin: 0 -5px; }
|
||||
.modal__tabs .modal__tab {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-right: 6.25px;
|
||||
padding: 5px;
|
||||
position: relative; }
|
||||
.modal__tabs .modal__tab:after {
|
||||
bottom: 0;
|
||||
content: '';
|
||||
height: 1px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
-webkit-transition: background 0.25s;
|
||||
transition: background 0.25s; }
|
||||
.modal__tabs .modal__tab:last-child {
|
||||
margin-right: 0; }
|
||||
.modal__tabs .modal__tab.is-active {
|
||||
color: #258de5;
|
||||
font-weight: 600; }
|
||||
.modal__tabs .modal__tab.is-active:after {
|
||||
background: #258de5; }
|
||||
.modal__content {
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
padding: 12.5px 25px; }
|
||||
.modal__content__wrapper {
|
||||
background: #f7fafc;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 0 1px rgba(26, 47, 61, 0.1), 0 0 35px rgba(26, 47, 61, 0.3);
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
left: 50%;
|
||||
max-height: 80%;
|
||||
max-width: 80%;
|
||||
overflow: auto;
|
||||
padding: 30px;
|
||||
position: absolute;
|
||||
top: 10%;
|
||||
-webkit-transform: translate(-50%, 0);
|
||||
@@ -1284,16 +1339,12 @@ body {
|
||||
.modal__content--align-center {
|
||||
text-align: center; }
|
||||
.modal__footer {
|
||||
margin-top: 25px; }
|
||||
margin-top: 25px;
|
||||
padding: 0 0 12.5px 0; }
|
||||
.modal__button-group {
|
||||
text-align: right; }
|
||||
.modal__button-group .button + .button {
|
||||
margin-left: 20px; }
|
||||
.modal__header {
|
||||
color: #313436;
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
margin-bottom: 20px; }
|
||||
.modal__animation-enter {
|
||||
opacity: 0; }
|
||||
.modal__animation-enter-active {
|
||||
@@ -1582,6 +1633,9 @@ body {
|
||||
.textbox-repeater .textbox__wrapper {
|
||||
position: relative; }
|
||||
|
||||
.textbox-repeater .form__row + .form__row {
|
||||
margin-top: 12.5px; }
|
||||
|
||||
.application__panel--torrent-details {
|
||||
background: #0e2231; }
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user