From 3b6c973ee8dacadb1c0a839e93504cd36088d7ca Mon Sep 17 00:00:00 2001 From: Iris Scholten Date: Thu, 31 May 2018 18:53:42 -0700 Subject: [PATCH] WIP Create DragAndDrop component Co-authored-by: Alex Paxton --- .../components/WriteDataBody.tsx | 147 ++++++------ .../components/WriteDataFooter.tsx | 5 +- .../components/WriteDataForm.tsx | 77 +++---- .../components/WriteDataHeader.tsx | 10 +- ui/src/shared/components/DragAndDrop.tsx | 212 ++++++++++++++++++ ui/src/style/chronograf.scss | 1 + ui/src/style/components/drag-and-drop.scss | 21 ++ ui/src/style/components/write-data-form.scss | 22 +- 8 files changed, 368 insertions(+), 127 deletions(-) create mode 100644 ui/src/shared/components/DragAndDrop.tsx create mode 100644 ui/src/style/components/drag-and-drop.scss diff --git a/ui/src/data_explorer/components/WriteDataBody.tsx b/ui/src/data_explorer/components/WriteDataBody.tsx index 9923a0bb7..2ba7d7454 100644 --- a/ui/src/data_explorer/components/WriteDataBody.tsx +++ b/ui/src/data_explorer/components/WriteDataBody.tsx @@ -7,13 +7,14 @@ import React, { ReactElement, } from 'react' import WriteDataFooter from 'src/data_explorer/components/WriteDataFooter' +import DragAndDrop from 'src/shared/components/DragAndDrop' interface Props { handleCancelFile: (e: MouseEvent) => void handleEdit: (e: ChangeEvent) => void handleKeyUp: (e: KeyboardEvent) => void handleFile: (drop: boolean) => (e: DragEvent) => void - handleSubmit: (e: MouseEvent) => void + handleSubmit: (uploadContent: string) => void inputContent: string uploadContent: string fileName: string @@ -33,9 +34,9 @@ class WriteDataBody extends PureComponent { ) } - private handleFile = (e: any): void => { - this.props.handleFile(false)(e) - } + // private handleFile = (e: any): void => { + // this.props.handleFile(false)(e) + // } private get input(): JSX.Element { const {isManual} = this.props @@ -43,7 +44,13 @@ class WriteDataBody extends PureComponent { return this.textarea } - return this.dragArea + // return this.dragArea + return ( + + ) } private get textarea(): ReactElement { @@ -62,86 +69,80 @@ class WriteDataBody extends PureComponent { ) } - private get dragArea(): ReactElement { - const {fileInput, handleFileOpen} = this.props + // private get dragArea(): ReactElement { + // const {fileInput, handleFileOpen} = this.props - return ( -
- {this.dragAreaHeader} -
- - {this.buttons} -
- ) - } + // return ( + //
+ // {this.dragAreaHeader} + //
+ // + // {this.buttons} + //
+ // ) + // } - private get dragAreaHeader(): ReactElement { - const {uploadContent, fileName} = this.props + // private get dragAreaHeader(): ReactElement { + // const {uploadContent, fileName} = this.props - if (uploadContent) { - return

{fileName}

- } + // if (uploadContent) { + // return

{fileName}

+ // } - return ( -

- Drop a file here or click to upload -

- ) - } + // return ( + //

+ // Drop a file here or click to upload + //

+ // ) + // } - private get infoClass(): string { - const {uploadContent} = this.props + // private get infoClass(): string { + // const {uploadContent} = this.props - if (uploadContent) { - return 'write-data-form--graphic write-data-form--graphic_success' - } + // if (uploadContent) { + // return 'write-data-form--graphic write-data-form--graphic_success' + // } - return 'write-data-form--graphic' - } + // return 'write-data-form--graphic' + // } - private get buttons(): ReactElement | null { - const {uploadContent, handleSubmit, handleCancelFile} = this.props + // private get buttons(): ReactElement | null { + // const {uploadContent, handleSubmit, handleCancelFile} = this.props - if (!uploadContent) { - return null - } + // if (!uploadContent) { + // return null + // } - return ( - - - - - ) - } + // return ( + // + // + // + // + // ) + // } - private get dragAreaClass(): string { - const {uploadContent} = this.props + // private get dragAreaClass(): string { + // const {uploadContent} = this.props - if (uploadContent) { - return 'write-data-form--file' - } + // if (uploadContent) { + // return 'write-data-form--file' + // } - return 'write-data-form--file write-data-form--file_active' - } + // return 'write-data-form--file write-data-form--file_active' + // } private get footer(): JSX.Element | null { - const { - isUploading, - isManual, - inputContent, - handleSubmit, - uploadContent, - } = this.props + const {isUploading, isManual, inputContent, uploadContent} = this.props if (!isManual) { return null @@ -152,11 +153,15 @@ class WriteDataBody extends PureComponent { isUploading={isUploading} isManual={isManual} inputContent={inputContent} - handleSubmit={handleSubmit} + handleSubmit={this.handleSubmit} uploadContent={uploadContent} /> ) } + + private handleSubmit = (): void => { + this.props.handleSubmit('') + } } export default WriteDataBody diff --git a/ui/src/data_explorer/components/WriteDataFooter.tsx b/ui/src/data_explorer/components/WriteDataFooter.tsx index c424bf631..5cb2d6930 100644 --- a/ui/src/data_explorer/components/WriteDataFooter.tsx +++ b/ui/src/data_explorer/components/WriteDataFooter.tsx @@ -1,4 +1,4 @@ -import React, {PureComponent, MouseEvent} from 'react' +import React, {PureComponent} from 'react' import { WRITE_DATA_DOCS_LINK, DATA_IMPORT_DOCS_LINK, @@ -12,7 +12,8 @@ interface Props { isUploading: boolean uploadContent: string inputContent: string - handleSubmit: (e: MouseEvent) => void + // handleSubmit: (e: MouseEvent) => void + handleSubmit: () => void } class WriteDataFooter extends PureComponent { diff --git a/ui/src/data_explorer/components/WriteDataForm.tsx b/ui/src/data_explorer/components/WriteDataForm.tsx index 0ea6d9025..dc9e98b12 100644 --- a/ui/src/data_explorer/components/WriteDataForm.tsx +++ b/ui/src/data_explorer/components/WriteDataForm.tsx @@ -1,10 +1,9 @@ import React, { PureComponent, - DragEvent, + // DragEvent, ChangeEvent, KeyboardEvent, } from 'react' -import classnames from 'classnames' import OnClickOutside from 'src/shared/components/OnClickOutside' import WriteDataBody from 'src/data_explorer/components/WriteDataBody' @@ -13,7 +12,7 @@ import WriteDataHeader from 'src/data_explorer/components/WriteDataHeader' import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames' import {ErrorHandling} from 'src/shared/decorators/errors' import {Source, DropdownItem} from 'src/types' -let dragCounter = 0 +// let dragCounter = 0 interface Props { source: Source @@ -54,17 +53,9 @@ class WriteDataForm extends PureComponent { public render() { const {onClose, errorThrown, source} = this.props - const {dragClass} = this.state return ( -
+
{ } } - private handleSubmit = async () => { - const {onClose, source, writeLineProtocol} = this.props - const {inputContent, uploadContent, selectedDatabase, isManual} = this.state - const content = isManual ? inputContent : uploadContent - this.setState({isUploading: true}) + private handleSubmit = async (uploadContent: string) => { + console.log('submit!', uploadContent) - try { - await writeLineProtocol(source, selectedDatabase, content) - this.setState({isUploading: false}) - onClose() - window.location.reload() - } catch (error) { - this.setState({isUploading: false}) - console.error(error.data.error) - } + // const {onClose, source, writeLineProtocol} = this.props + // const {inputContent, uploadContent, selectedDatabase, isManual} = this.state + // const content = isManual ? inputContent : uploadContent + // this.setState({isUploading: true}) + + // try { + // await writeLineProtocol(source, selectedDatabase, content) + // this.setState({isUploading: false}) + // onClose() + // window.location.reload() + // } catch (error) { + // this.setState({isUploading: false}) + // console.error(error.data.error) + // } } private handleEdit = (e: ChangeEvent): void => { @@ -159,24 +152,24 @@ class WriteDataForm extends PureComponent { this.fileInput.value = '' } - private handleDragOver = (e: DragEvent) => { - e.preventDefault() - e.stopPropagation() - } + // private handleDragOver = (e: DragEvent) => { + // e.preventDefault() + // e.stopPropagation() + // } - private handleDragEnter = (e: DragEvent): void => { - dragCounter += 1 - e.preventDefault() - this.setState({dragClass: 'drag-over'}) - } + // private handleDragEnter = (e: DragEvent): void => { + // dragCounter += 1 + // e.preventDefault() + // this.setState({dragClass: 'drag-over'}) + // } - private handleDragLeave = (e: DragEvent): void => { - dragCounter -= 1 - e.preventDefault() - if (dragCounter === 0) { - this.setState({dragClass: 'drag-none'}) - } - } + // private handleDragLeave = (e: DragEvent): void => { + // dragCounter -= 1 + // e.preventDefault() + // if (dragCounter === 0) { + // this.setState({dragClass: 'drag-none'}) + // } + // } private handleFileOpen = (): void => { const {uploadContent} = this.state diff --git a/ui/src/data_explorer/components/WriteDataHeader.tsx b/ui/src/data_explorer/components/WriteDataHeader.tsx index 73304a75f..6a99d9c4f 100644 --- a/ui/src/data_explorer/components/WriteDataHeader.tsx +++ b/ui/src/data_explorer/components/WriteDataHeader.tsx @@ -32,7 +32,10 @@ class WriteDataHeader extends PureComponent { database={selectedDatabase} onErrorThrown={errorThrown} /> -
    +
    • File Upload
    • @@ -45,7 +48,10 @@ class WriteDataHeader extends PureComponent {
-
+
diff --git a/ui/src/shared/components/DragAndDrop.tsx b/ui/src/shared/components/DragAndDrop.tsx new file mode 100644 index 000000000..d09733cef --- /dev/null +++ b/ui/src/shared/components/DragAndDrop.tsx @@ -0,0 +1,212 @@ +import React, {PureComponent, ReactElement, DragEvent} from 'react' + +interface Props { + fileTypesToAccept?: string + containerClass?: string + handleSubmit: (uploadContent: string) => void +} + +interface State { + inputContent: string | null + uploadContent: string + fileName: string + progress: string + dragClass: string + isUploading: boolean +} + +let dragCounter = 0 +class DragAndDrop extends PureComponent { + private fileInput: HTMLInputElement + + constructor(props: Props) { + super(props) + + this.state = { + inputContent: null, + uploadContent: '', + fileName: '', + progress: '', + dragClass: 'drag-none', + isUploading: false, + } + } + + public render() { + return ( +
+ {/* (Invisible, covers entire screen) + This div handles drag only*/} +
+ {/* visible form, handles drag & click */} + {this.dragArea} +
+ ) + } + + private get dragArea(): ReactElement { + return ( +
+ {this.dragAreaHeader} +
+ (this.fileInput = r)} + className="write-data-form--upload" + accept={this.fileTypesToAccept} + onChange={this.handleFile(false)} + /> + {this.buttons} +
+ ) + } + + private get fileTypesToAccept(): string { + const {fileTypesToAccept} = this.props + + if (!fileTypesToAccept) { + return '*' + } + + return fileTypesToAccept + } + + private get infoClass(): string { + const {uploadContent} = this.state + + if (uploadContent) { + return 'write-data-form--graphic write-data-form--graphic_success' + } + + return 'write-data-form--graphic' + } + + private get dragAreaClass(): string { + const {uploadContent} = this.state + + if (uploadContent) { + return 'drag-and-drop--form' + } + + return 'drag-and-drop--form active' + } + + private get dragAreaHeader(): ReactElement { + const {uploadContent, fileName} = this.state + + if (uploadContent) { + return

{fileName}

+ } + + return ( +

+ Drop a file here or click to upload +

+ ) + } + + private get buttons(): ReactElement | null { + const {uploadContent} = this.state + + if (!uploadContent) { + return null + } + + return ( + + + + + ) + } + + private handleSubmit = () => { + const {handleSubmit} = this.props + const {uploadContent} = this.state + + handleSubmit(uploadContent) + } + + private handleFile = (drop: boolean) => (e: any): void => { + let file + if (drop) { + file = e.dataTransfer.files[0] + this.setState({ + dragClass: 'drag-none', + }) + } else { + file = e.currentTarget.files[0] + } + + if (!file) { + return + } + + e.preventDefault() + e.stopPropagation() + + const reader = new FileReader() + reader.readAsText(file) + reader.onload = loadEvent => { + this.setState({ + uploadContent: loadEvent.target.result, + fileName: file.name, + }) + } + } + + private handleFileOpen = (): void => { + const {uploadContent} = this.state + if (uploadContent === '') { + this.fileInput.click() + } + } + + private handleCancelFile = (): void => { + this.setState({uploadContent: ''}) + this.fileInput.value = '' + } + + private handleDragOver = (e: DragEvent) => { + e.preventDefault() + e.stopPropagation() + } + + private handleDragEnter = (e: DragEvent): void => { + dragCounter += 1 + e.preventDefault() + this.setState({dragClass: 'drag-over'}) + } + + private handleDragLeave = (e: DragEvent): void => { + dragCounter -= 1 + e.preventDefault() + if (dragCounter === 0) { + this.setState({dragClass: 'drag-none'}) + } + } +} + +export default DragAndDrop diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index 5c1300cbe..6657dbd77 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -41,6 +41,7 @@ @import 'components/color-dropdown'; @import 'components/custom-time-range'; @import 'components/customize-fields'; +@import 'components/drag-and-drop'; @import 'components/dygraphs'; @import 'components/fancy-scrollbars'; @import 'components/fancy-table'; diff --git a/ui/src/style/components/drag-and-drop.scss b/ui/src/style/components/drag-and-drop.scss new file mode 100644 index 000000000..0e8d20141 --- /dev/null +++ b/ui/src/style/components/drag-and-drop.scss @@ -0,0 +1,21 @@ +/* + Drag and Drop Styles + ------------------------------------------------------------------------------ +*/ + +$drag-and-drop--z-dropzone: 9000; +$drag-and-drop--z-form: 9010; + +.drag-and-drop--dropzone { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: $drag-and-drop--z-dropzone; +} + +.drag-and-drop--form { + position: relative; + z-index: $drag-and-drop--z-form; +} diff --git a/ui/src/style/components/write-data-form.scss b/ui/src/style/components/write-data-form.scss index 6f9582567..1b00142c3 100644 --- a/ui/src/style/components/write-data-form.scss +++ b/ui/src/style/components/write-data-form.scss @@ -9,14 +9,14 @@ $write-data--drag-overlay-z: 1; $write-data--max-width: 960px; $write-data--gutter: 30px; $write-data--margin: 18px; -$write-data--input-height: calc(90vh - 48px - 60px - 36px); // Heights of everything but input height +$write-data--input-height: calc( + 90vh - 48px - 60px - 36px +); // Heights of everything but input height $write-data--transition: opacity 0.4s ease; .write-data-form { max-width: $write-data--max-width; margin: 0 auto; - position: relative; - z-index: $write-data--form-z; } .write-data-form--header { height: $chronograf-page-header-height; @@ -40,7 +40,7 @@ $write-data--transition: opacity 0.4s ease; .write-data-form--body { padding: $write-data--margin $write-data--gutter; border-radius: 0 0 $radius $radius; - @include gradient-v($g2-kevlar,$g0-obsidian); + @include gradient-v($g2-kevlar, $g0-obsidian); } textarea.form-control.write-data-form--input { height: $write-data--input-height; @@ -76,9 +76,7 @@ textarea.form-control.write-data-form--input { border: 2px solid $g4-onyx; margin-bottom: 48px; border-radius: 3px; - transition: - background-color 0.25s ease, - border-color 0.25s ease; + transition: background-color 0.25s ease, border-color 0.25s ease; > p { color: $g12-forge; @@ -100,7 +98,7 @@ textarea.form-control.write-data-form--input { } } -input[type="file"].write-data-form--upload { +input[type='file'].write-data-form--upload { display: none; } .write-data-form--filepath_selected, @@ -113,8 +111,12 @@ input[type="file"].write-data-form--upload { text-overflow: ellipsis; margin: 0 0 30px 0; } -.write-data-form--filepath_empty {color: $g12-forge;} -.write-data-form--filepath_selected {color: $c-rainforest;} +.write-data-form--filepath_empty { + color: $g12-forge; +} +.write-data-form--filepath_selected { + color: $c-rainforest; +} .write-data-form--file-submit { margin-top: 30px;