parent
b24a40797f
commit
6275737ced
@ -0,0 +1,35 @@
|
|||||||
|
const loose = true;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
// Stage 1
|
||||||
|
'@babel/plugin-proposal-export-default-from',
|
||||||
|
['@babel/plugin-proposal-optional-chaining', { loose }],
|
||||||
|
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
|
||||||
|
|
||||||
|
// Stage 2
|
||||||
|
'@babel/plugin-proposal-export-namespace-from',
|
||||||
|
|
||||||
|
// Stage 3
|
||||||
|
['@babel/plugin-proposal-class-properties', { loose }],
|
||||||
|
'@babel/plugin-syntax-dynamic-import'
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
development: {
|
||||||
|
presets: [
|
||||||
|
['@babel/preset-react', { development: true }]
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
'babel-plugin-inline-classnames'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
presets: [
|
||||||
|
'@babel/preset-react'
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
'babel-plugin-transform-react-remove-prop-types'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = [
|
||||||
|
'>0.25%',
|
||||||
|
'not ie 11',
|
||||||
|
'not op_mini all',
|
||||||
|
'not chrome < 60'
|
||||||
|
];
|
@ -1,8 +1,5 @@
|
|||||||
require('./build.js');
|
require('./build.js');
|
||||||
require('./clean.js');
|
require('./clean.js');
|
||||||
require('./copy.js');
|
require('./copy.js');
|
||||||
require('./imageMin.js');
|
|
||||||
require('./start.js');
|
|
||||||
require('./stripBom.js');
|
|
||||||
require('./watch.js');
|
require('./watch.js');
|
||||||
require('./webpack.js');
|
require('./webpack.js');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const gulpUtil = require('gulp-util');
|
const colors = require('ansi-colors');
|
||||||
|
|
||||||
module.exports = function errorHandler(error) {
|
module.exports = function errorHandler(error) {
|
||||||
gulpUtil.log(gulpUtil.colors.red(`Error (${error.plugin}): ${error.message}`));
|
console.log(colors.red(`Error (${error.plugin}): ${error.message}`));
|
||||||
this.emit('end');
|
this.emit('end');
|
||||||
};
|
};
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const rootPath = path.resolve(__dirname + '/../../src/');
|
|
||||||
module.exports = function(source) {
|
|
||||||
if (this.cacheable) {
|
|
||||||
this.cacheable();
|
|
||||||
}
|
|
||||||
|
|
||||||
const resourcePath = this.resourcePath.replace(rootPath, '');
|
|
||||||
const wrappedSource =`
|
|
||||||
<!-- begin ${resourcePath} -->
|
|
||||||
${source}
|
|
||||||
<!-- end ${resourcePath} -->`;
|
|
||||||
|
|
||||||
return wrappedSource;
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
var gulp = require('gulp');
|
|
||||||
var print = require('gulp-print').default;
|
|
||||||
var paths = require('./helpers/paths.js');
|
|
||||||
|
|
||||||
gulp.task('imageMin', () => {
|
|
||||||
var imagemin = require('gulp-imagemin');
|
|
||||||
return gulp.src(paths.src.images)
|
|
||||||
.pipe(imagemin({
|
|
||||||
progressive: false,
|
|
||||||
optimizationLevel: 4,
|
|
||||||
svgoPlugins: [{ removeViewBox: false }]
|
|
||||||
}))
|
|
||||||
.pipe(print())
|
|
||||||
.pipe(gulp.dest(paths.src.content + 'Images/'));
|
|
||||||
});
|
|
@ -1,104 +0,0 @@
|
|||||||
// will download and run radarr (server) in a non-windows enviroment
|
|
||||||
// you can use this if you don't care about the server code and just want to work
|
|
||||||
// with the web code.
|
|
||||||
|
|
||||||
var http = require('http');
|
|
||||||
var gulp = require('gulp');
|
|
||||||
var fs = require('fs');
|
|
||||||
var targz = require('tar.gz');
|
|
||||||
var del = require('del');
|
|
||||||
var spawn = require('child_process').spawn;
|
|
||||||
|
|
||||||
function download(url, dest, cb) {
|
|
||||||
console.log('Downloading ' + url + ' to ' + dest);
|
|
||||||
var file = fs.createWriteStream(dest);
|
|
||||||
http.get(url, function(response) {
|
|
||||||
response.pipe(file);
|
|
||||||
file.on('finish', function() {
|
|
||||||
console.log('Download completed');
|
|
||||||
file.close(cb);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLatest(cb) {
|
|
||||||
var branch = 'develop';
|
|
||||||
process.argv.forEach(function(val) {
|
|
||||||
var branchMatch = /branch=([\S]*)/.exec(val);
|
|
||||||
if (branchMatch && branchMatch.length > 1) {
|
|
||||||
branch = branchMatch[1];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var url = 'http://radarr.aeonlucid.com/v1/update/' + branch + '?os=osx';
|
|
||||||
|
|
||||||
console.log('Checking for latest version:', url);
|
|
||||||
|
|
||||||
http.get(url, function(res) {
|
|
||||||
var data = '';
|
|
||||||
|
|
||||||
res.on('data', function(chunk) {
|
|
||||||
data += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
res.on('end', function() {
|
|
||||||
var updatePackage = JSON.parse(data).updatePackage;
|
|
||||||
console.log('Latest version available: ' + updatePackage.version + ' Release Date: ' + updatePackage.releaseDate);
|
|
||||||
cb(updatePackage);
|
|
||||||
});
|
|
||||||
}).on('error', function(e) {
|
|
||||||
console.log('problem with request: ' + e.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function extract(source, dest, cb) {
|
|
||||||
console.log('extracting download page to ' + dest);
|
|
||||||
new targz().extract(source, dest, function(err) {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
console.log('Update package extracted.');
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.task('getSonarr', function() {
|
|
||||||
try {
|
|
||||||
fs.mkdirSync('./_start/');
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code !== 'EEXIST') {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getLatest(function(updatePackage) {
|
|
||||||
var packagePath = './_start/' + updatePackage.filename;
|
|
||||||
var dirName = './_start/' + updatePackage.version;
|
|
||||||
download(updatePackage.url, packagePath, function() {
|
|
||||||
extract(packagePath, dirName, function() {
|
|
||||||
// clean old binaries
|
|
||||||
console.log('Cleaning old binaries');
|
|
||||||
del.sync(['./_output/*', '!./_output/UI/']);
|
|
||||||
console.log('copying binaries to target');
|
|
||||||
gulp.src(dirName + '/NzbDrone/*.*')
|
|
||||||
.pipe(gulp.dest('./_output/'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('startSonarr', function() {
|
|
||||||
var ls = spawn('mono', ['--debug', './_output/Radarr.exe']);
|
|
||||||
|
|
||||||
ls.stdout.on('data', function(data) {
|
|
||||||
process.stdout.write(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
ls.stderr.on('data', function(data) {
|
|
||||||
process.stdout.write(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
ls.on('close', function(code) {
|
|
||||||
console.log('child process exited with code ' + code);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,13 +0,0 @@
|
|||||||
const gulp = require('gulp');
|
|
||||||
const paths = require('./helpers/paths.js');
|
|
||||||
const stripbom = require('gulp-stripbom');
|
|
||||||
|
|
||||||
function stripBom(dest) {
|
|
||||||
gulp.src([paths.src.scripts, paths.src.exclude.libs])
|
|
||||||
.pipe(stripbom({ showLog: false }))
|
|
||||||
.pipe(gulp.dest(dest));
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.task('stripBom', () => {
|
|
||||||
stripBom(paths.src.root);
|
|
||||||
});
|
|
@ -1,27 +1,18 @@
|
|||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
const livereload = require('gulp-livereload');
|
const livereload = require('gulp-livereload');
|
||||||
const watch = require('gulp-watch');
|
const gulpWatch = require('gulp-watch');
|
||||||
const paths = require('./helpers/paths.js');
|
const paths = require('./helpers/paths.js');
|
||||||
|
|
||||||
require('./copy.js');
|
require('./copy.js');
|
||||||
require('./webpack.js');
|
require('./webpack.js');
|
||||||
|
|
||||||
function watchTask(glob, task) {
|
function watch() {
|
||||||
const options = {
|
|
||||||
name: `watch: ${task}`,
|
|
||||||
verbose: true
|
|
||||||
};
|
|
||||||
return watch(glob, options, () => {
|
|
||||||
gulp.start(task);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.task('watch', ['copyHtml', 'copyFonts', 'copyImages', 'copyJs'], () => {
|
|
||||||
livereload.listen({ start: true });
|
livereload.listen({ start: true });
|
||||||
|
|
||||||
gulp.start('webpackWatch');
|
gulp.task('webpackWatch')();
|
||||||
|
gulpWatch(paths.src.html, gulp.series('copyHtml'));
|
||||||
|
gulpWatch(`${paths.src.fonts}**/*.*`, gulp.series('copyFonts'));
|
||||||
|
gulpWatch(`${paths.src.images}**/*.*`, gulp.series('copyImages'));
|
||||||
|
}
|
||||||
|
|
||||||
watchTask(paths.src.html, 'copyHtml');
|
gulp.task('watch', gulp.series('build', watch));
|
||||||
watchTask(`${paths.src.fonts}**/*.*`, 'copyFonts');
|
|
||||||
watchTask(`${paths.src.images}**/*.*`, 'copyImages');
|
|
||||||
});
|
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
.language,
|
.language,
|
||||||
.quality {
|
.quality {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indexer {
|
.indexer {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.description {
|
.description {
|
||||||
composes: title from 'Components/DescriptionList/DescriptionListItemDescription.css';
|
composes: title from '~Components/DescriptionList/DescriptionListItemDescription.css';
|
||||||
|
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.markAsFailedButton {
|
.markAsFailedButton {
|
||||||
composes: button from 'Components/Link/Button.css';
|
composes: button from '~Components/Link/Button.css';
|
||||||
|
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
.downloadClient {
|
.downloadClient {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 120px;
|
width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indexer {
|
.indexer {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.releaseGroup {
|
.releaseGroup {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 110px;
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
.quality {
|
.quality {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.protocol {
|
.protocol {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress {
|
.progress {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.status {
|
.status {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.timeleft {
|
.timeleft {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
.input {
|
.input {
|
||||||
composes: input from 'Components/Form/CheckInput.css';
|
composes: input from '~Components/Form/CheckInput.css';
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
.link {
|
.link {
|
||||||
composes: link from 'Components/Link/Link.css';
|
composes: link from '~Components/Link/Link.css';
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.freeSpace,
|
.freeSpace,
|
||||||
.unmappedFolders {
|
.unmappedFolders {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 45px;
|
width: 45px;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
export const DAY = 'day';
|
||||||
|
export const WEEK = 'week';
|
||||||
export const MONTH = 'month';
|
export const MONTH = 'month';
|
||||||
|
export const FORECAST = 'forecast';
|
||||||
export const AGENDA = 'agenda';
|
export const AGENDA = 'agenda';
|
||||||
|
|
||||||
export const all = [MONTH, AGENDA];
|
export const all = [DAY, WEEK, MONTH, FORECAST, AGENDA];
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.modal {
|
.modal {
|
||||||
composes: modal from 'Components/Modal/Modal.css';
|
composes: modal from '~Components/Modal/Modal.css';
|
||||||
|
|
||||||
height: 600px;
|
height: 600px;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.type {
|
.type {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
.inputContainer {
|
||||||
|
composes: input from '~Components/Form/Input.css';
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
min-height: 35px;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
&.isFocused {
|
||||||
|
outline: 0;
|
||||||
|
border-color: $inputFocusBorderColor;
|
||||||
|
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputFocusBoxShadowColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasError {
|
||||||
|
composes: hasError from '~Components/Form/Input.css';
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasWarning {
|
||||||
|
composes: hasWarning from '~Components/Form/Input.css';
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import KeyValueListInputItem from './KeyValueListInputItem';
|
||||||
|
import styles from './KeyValueListInput.css';
|
||||||
|
|
||||||
|
class KeyValueListInput extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isFocused: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onItemChange = (index, itemValue) => {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
onChange
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const newValue = [...value];
|
||||||
|
|
||||||
|
if (index == null) {
|
||||||
|
newValue.push(itemValue);
|
||||||
|
} else {
|
||||||
|
newValue.splice(index, 1, itemValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange({
|
||||||
|
name,
|
||||||
|
value: newValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemoveItem = (index) => {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
onChange
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const newValue = [...value];
|
||||||
|
newValue.splice(index, 1);
|
||||||
|
|
||||||
|
onChange({
|
||||||
|
name,
|
||||||
|
value: newValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus = () => {
|
||||||
|
this.setState({
|
||||||
|
isFocused: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur = () => {
|
||||||
|
this.setState({
|
||||||
|
isFocused: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
onChange
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const newValue = value.reduce((acc, v) => {
|
||||||
|
if (v.key || v.value) {
|
||||||
|
acc.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (newValue.length !== value.length) {
|
||||||
|
onChange({
|
||||||
|
name,
|
||||||
|
value: newValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
value,
|
||||||
|
keyPlaceholder,
|
||||||
|
valuePlaceholder
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { isFocused } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames(
|
||||||
|
className,
|
||||||
|
isFocused && styles.isFocused
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
[...value, { key: '', value: '' }].map((v, index) => {
|
||||||
|
return (
|
||||||
|
<KeyValueListInputItem
|
||||||
|
key={index}
|
||||||
|
index={index}
|
||||||
|
keyValue={v.key}
|
||||||
|
value={v.value}
|
||||||
|
keyPlaceholder={keyPlaceholder}
|
||||||
|
valuePlaceholder={valuePlaceholder}
|
||||||
|
isNew={index === value.length}
|
||||||
|
onChange={this.onItemChange}
|
||||||
|
onRemove={this.onRemoveItem}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onBlur={this.onBlur}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyValueListInput.propTypes = {
|
||||||
|
className: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
hasError: PropTypes.bool,
|
||||||
|
hasWarning: PropTypes.bool,
|
||||||
|
keyPlaceholder: PropTypes.string,
|
||||||
|
valuePlaceholder: PropTypes.string,
|
||||||
|
onChange: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
KeyValueListInput.defaultProps = {
|
||||||
|
className: styles.inputContainer,
|
||||||
|
value: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KeyValueListInput;
|
@ -0,0 +1,14 @@
|
|||||||
|
.itemContainer {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
border-bottom: 1px solid $inputBorderColor;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyInput,
|
||||||
|
.valueInput {
|
||||||
|
border: none;
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import IconButton from 'Components/Link/IconButton';
|
||||||
|
import TextInput from './TextInput';
|
||||||
|
import styles from './KeyValueListInputItem.css';
|
||||||
|
|
||||||
|
class KeyValueListInputItem extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onKeyChange = ({ value: keyValue }) => {
|
||||||
|
const {
|
||||||
|
index,
|
||||||
|
value,
|
||||||
|
onChange
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
onChange(index, { key: keyValue, value });
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChange = ({ value }) => {
|
||||||
|
// TODO: Validate here or validate at a lower level component
|
||||||
|
|
||||||
|
const {
|
||||||
|
index,
|
||||||
|
keyValue,
|
||||||
|
onChange
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
onChange(index, { key: keyValue, value });
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemovePress = () => {
|
||||||
|
const {
|
||||||
|
index,
|
||||||
|
onRemove
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
onRemove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus = () => {
|
||||||
|
this.props.onFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur = () => {
|
||||||
|
this.props.onBlur();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
keyValue,
|
||||||
|
value,
|
||||||
|
keyPlaceholder,
|
||||||
|
valuePlaceholder,
|
||||||
|
isNew
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.itemContainer}>
|
||||||
|
<TextInput
|
||||||
|
className={styles.keyInput}
|
||||||
|
name="key"
|
||||||
|
value={keyValue}
|
||||||
|
placeholder={keyPlaceholder}
|
||||||
|
onChange={this.onKeyChange}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onBlur={this.onBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
className={styles.valueInput}
|
||||||
|
name="value"
|
||||||
|
value={value}
|
||||||
|
placeholder={valuePlaceholder}
|
||||||
|
onChange={this.onValueChange}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onBlur={this.onBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
!isNew &&
|
||||||
|
<IconButton
|
||||||
|
name={icons.REMOVE}
|
||||||
|
tabIndex={-1}
|
||||||
|
onPress={this.onRemovePress}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyValueListInputItem.propTypes = {
|
||||||
|
index: PropTypes.number,
|
||||||
|
keyValue: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
|
keyPlaceholder: PropTypes.string.isRequired,
|
||||||
|
valuePlaceholder: PropTypes.string.isRequired,
|
||||||
|
isNew: PropTypes.bool.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
onRemove: PropTypes.func.isRequired,
|
||||||
|
onFocus: PropTypes.func.isRequired,
|
||||||
|
onBlur: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
KeyValueListInputItem.defaultProps = {
|
||||||
|
keyPlaceholder: 'Key',
|
||||||
|
valuePlaceholder: 'Value'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KeyValueListInputItem;
|
@ -1,5 +1,5 @@
|
|||||||
.input {
|
.input {
|
||||||
composes: input from 'Components/Form/TextInput.css';
|
composes: input from '~Components/Form/TextInput.css';
|
||||||
|
|
||||||
font-family: $passwordFamily;
|
font-family: $passwordFamily;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
.label {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 2px;
|
||||||
|
color: $white;
|
||||||
|
/** text-align: center; **/
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
color: $helpTextColor;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Kinds **/
|
||||||
|
|
||||||
|
/** Sizes **/
|
||||||
|
|
||||||
|
.small {
|
||||||
|
padding: 1px 3px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medium {
|
||||||
|
padding: 2px 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.large {
|
||||||
|
padding: 3px 7px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Outline **/
|
||||||
|
|
||||||
|
.outline {
|
||||||
|
background-color: $white;
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { kinds, sizes } from 'Helpers/Props';
|
||||||
|
import styles from './InfoLabel.css';
|
||||||
|
|
||||||
|
function InfoLabel(props) {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
title,
|
||||||
|
kind,
|
||||||
|
size,
|
||||||
|
outline,
|
||||||
|
children,
|
||||||
|
...otherProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={classNames(
|
||||||
|
className,
|
||||||
|
styles[kind],
|
||||||
|
styles[size],
|
||||||
|
outline && styles.outline
|
||||||
|
)}
|
||||||
|
{...otherProps}
|
||||||
|
>
|
||||||
|
<div className={styles.title}>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoLabel.propTypes = {
|
||||||
|
className: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
kind: PropTypes.oneOf(kinds.all).isRequired,
|
||||||
|
size: PropTypes.oneOf(sizes.all).isRequired,
|
||||||
|
outline: PropTypes.bool.isRequired,
|
||||||
|
children: PropTypes.node.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
InfoLabel.defaultProps = {
|
||||||
|
className: styles.label,
|
||||||
|
kind: kinds.DEFAULT,
|
||||||
|
size: sizes.SMALL,
|
||||||
|
outline: false
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InfoLabel;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue