Skip to content
This repository was archived by the owner on Sep 10, 2021. It is now read-only.

Commit

Permalink
feat(Upload body): allow users to upload a dataset body
Browse files Browse the repository at this point in the history
Merge pull request #449 from qri-io/uploadBody
  • Loading branch information
ramfox authored Jan 31, 2019
2 parents b4c474f + 9119fcb commit 4b64b38
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 550 deletions.
502 changes: 26 additions & 476 deletions app/dist/renderer.prod.js

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions app/dist/style.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 14 additions & 10 deletions lib/actions/dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,17 +284,21 @@ function prepFiles (dataset = {}, transformScript = '', vizScript = '', bodyData
})
}

if (bodyData && bodyData.length) {
console.log(bodyData, bodyData.length)
var name = 'body.'
if (dataset && dataset.structure && dataset.structure.format) {
name += dataset.structure.format
} else {
name += 'json'
if (bodyData) {
if (bodyData.constructor === File) {
body = bodyData
} else if (bodyData.length) {
console.log(bodyData, bodyData.length)
var name = 'body.'
if (dataset && dataset.structure && dataset.structure.format) {
name += dataset.structure.format
} else {
name += 'json'
}
body = new File([bodyData], name, {
type: 'text/plain'
})
}
body = new File([bodyData], name, {
type: 'text/plain'
})
}

return { file, transform, viz, body }
Expand Down
198 changes: 168 additions & 30 deletions lib/components/editor/EditBody.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* globals alert */
/* globals alert File confirm */

import React from 'react'
import PropTypes from 'prop-types'
Expand All @@ -19,7 +19,10 @@ import cloneDeep from 'clone-deep'

export default class EditBody extends Base {
constructor (props) {
super(props);
super(props)
this.state = {
dropZoneState: ''
};
[
'onAddBody',
'onRemoveBody',
Expand All @@ -36,12 +39,24 @@ export default class EditBody extends Base {
'onMoveColumns',
'setReOrder',
'onMoveRows',
'onCollapseChanges'
'onCollapseChanges',
'handleSetBodyFile',
'handleDrop',
'handleDragOver',
'handleDragEnd',
'handleDragLeave',
'handleUploadBodyFile'
].forEach((m) => { this[m] = this[m].bind(this) })
}

componentWillMount () {
const { bodyView, structure, body: prevBody, columnHeaders, colOrder, rowOrder } = this.props
if (prevBody && prevBody.constructor === File) {
// if we have a body file, we don't need to ensure the schema matches
// the user inputed body, because that body will have been erased
// as soon as the user uploaded a body file
return
}
const check = generateMatchingSchemaAndBody(bodyView, structure, prevBody, columnHeaders, colOrder, rowOrder)
if (typeof check === 'string') {
this.props.setBodyError(check)
Expand All @@ -66,7 +81,8 @@ export default class EditBody extends Base {
}

componentWillUnmount () {
if (this.props.bodyView === 'table') {
const { body, bodyView } = this.props
if (body && body.constructor !== File && bodyView === 'table') {
this.onCollapseChanges()
}
}
Expand All @@ -82,6 +98,64 @@ export default class EditBody extends Base {
}
}

handleDragOver (e) {
e.stopPropagation()
e.preventDefault()
this.setState({ dropZoneState: 'dragOver' })
}

handleDragLeave (e) {
e.stopPropagation()
e.preventDefault()
this.setState({ dropZoneState: '' })
}

handleDrop (e) {
e.preventDefault()
const dt = e.dataTransfer
let file = dt.items[0]
if (file.kind === 'file') {
file = file.getAsFile()
this.handleSetBodyFile([file])
this.setState({ dropZoneState: '' })
return
}
alert('Files must be in csv, json, or cbor format')
this.setState({ dropZoneState: '' })
}

handleDragEnd (e) {
e.preventDefault()
let dt = e.dataTransfer
if (dt.items) {
for (var i = 0; i < dt.items.length; i++) {
dt.items.remove(i)
}
} else {
dt.clearData()
}
}

handleUploadBodyFile (e) {
this.handleSetBodyFile(e.target.files)
}

handleSetBodyFile (files) {
if (files.length > 1) {
alert('error, only one file allowed')
return
} else if (files[0].name.endsWith('.csv') ||
files[0].name.endsWith('.json') ||
files[0].name.endsWith('.cbor')) {
var cont = confirm('Uploading a body file will remove any unsaved work on the previous body. Press ok to continue.')
if (cont) {
this.props.onSetBody(files[0])
}
return
}
alert('Files must be in csv, json, or cbor format')
}

setColumnHeaders (columnHeaders) {
this.props.setSchema({
type: 'array',
Expand Down Expand Up @@ -241,7 +315,7 @@ export default class EditBody extends Base {
this.setReOrder(rows, target, mapped, rowOrder, 'row')
}

renderEditor () {
renderEditor (css) {
const {
body,
bodyView,
Expand All @@ -252,6 +326,16 @@ export default class EditBody extends Base {
selectOnLineNumbers: true
}

if (body && body.constructor === File) {
return (
<div className={css('fileBox')}>
<div className={css('fileName')}>
<div className={css('remove')} onClick={this.onRemoveBody}>x</div>
{body.name}
</div>
</div>)
}

switch (bodyView) {
case 'json':
return (
Expand Down Expand Up @@ -306,42 +390,60 @@ export default class EditBody extends Base {
bodyError
} = this.props

if (body === undefined) {
return (
<div className={css('wrap')}>
<div className={css('center')}>
<Button onClick={this.onAddBody} color='b' text='Add Body' />
</div>
</div>
)
const { dropZoneState } = this.state
let style = css('wrap')
if (dropZoneState) {
style += style + ' ' + css(dropZoneState)
}

return (
<div className={css('wrap')}>
<header className={css('header')}>
<span style={{ float: 'right' }}>
<Button onClick={this.onRemoveBody} color='b' text='Remove' />
</span>
<div className={css('select')}>
<ValidSelect label='view' name='view' options={[{ value: 'table', text: 'table' }, { value: 'json', text: 'json editor' }]} onChange={this.onSetView} allowEmpty={false} labelTop value={bodyView} />
<div
className={style}
onDrop={this.handleDrop}
onDragOver={this.handleDragOver}
onDragEnd={this.handleDragEnd}
onDragLeave={this.handleDragLeave} >
{ body === undefined
? <div className={css('center')}>
<Button onClick={this.onAddBody} color='b' text='Add Body' />
</div>
</header>
<div className={`${css('body')} editBody`}>
{bodyError
? <div className={css('error')}>
{bodyError}
<br /><br />
Please adjust to view the data in <span style={{ fontWeight: 500 }}>table</span> mode.
: <div className={css('wrap')}>
<header className={css('header')}>
<span style={{ float: 'right' }}>
<label htmlFor='bodyFileUpload' className='btn'>Upload</label>
<input
className={css('displayNone')}
type='file'
name='bodyFile'
id='bodyFileUpload'
onChange={this.handleUploadBodyFile}
ref={(input) => { this.fileUpload = input }} />
<Button onClick={this.onRemoveBody} color='b' text='Remove' />
</span>
{ // if there is a body and it is a file, don't show the selector
body && !(body.constructor === File) &&
<div className={css('select')}>
<ValidSelect label='view' name='view' options={[{ value: 'table', text: 'table' }, { value: 'json', text: 'json editor' }]} onChange={this.onSetView} allowEmpty={false} labelTop value={bodyView} />
</div>}
</header>
<div className={`${css('body')} editBody`}>
{bodyError
? <div className={css('error')}>
{bodyError}
<br /><br />
Please adjust to view the data in <span style={{ fontWeight: 500 }}>table</span> mode.
</div>
: this.renderEditor(css)}
</div>
: this.renderEditor(css)}
</div>
</div>
}
</div>
)
}

styles () {
return {
wrap: {
position: 'relative',
width: '100%',
height: '100%',
display: 'flex',
Expand Down Expand Up @@ -376,6 +478,42 @@ export default class EditBody extends Base {
padding: '30px',
background: 'rgba(255, 255, 255, 0.1)',
borderRadius: 5
},
displayNone: {
display: 'none'
},
fileBox: {
width: '100%',
height: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
},
fileName: {
background: 'rgba(0, 0, 0, .25)',
position: 'relative',
border: '1px solid rgba(0, 0, 0, .50)',
color: 'white',
borderRadius: 8,
padding: '10px 20px'
},
remove: {
position: 'absolute',
top: '-7px',
right: '-7px',
cursor: 'pointer',
background: 'rgba(0, 0, 0, .50)',
borderRadius: '8px',
paddingRight: '4px',
paddingLeft: '4px',
color: 'white',
fontSize: '.7rem',
':hover': {
color: 'grey'
}
},
dragOver: {
background: 'rgba(255,255,255,0.3)'
}
}
}
Expand Down
Loading

0 comments on commit 4b64b38

Please sign in to comment.