-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(FileDropzone): add new FileDropzone component (#84)
![2018-11-30 16 03 56](https://user-images.githubusercontent.com/1527371/49276457-91f9e280-f4b9-11e8-83cf-ce5bc6f0353d.png) [DEMO](https://deploy-preview-84--mcs-ui.netlify.com/?selectedKind=FileDropzone&selectedStory=With%20file%20value&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybook%2Factions%2Factions-panel)
- Loading branch information
1 parent
429428a
commit 0d98840
Showing
15 changed files
with
1,731 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// flow-typed signature: 5fa4b5a2cd25d41c13602f88c23a60a0 | ||
// flow-typed version: <<STUB>>/react-input-files_v^1.0.0/flow_v0.87.0 | ||
|
||
/** | ||
* This is an autogenerated libdef stub for: | ||
* | ||
* 'react-input-files' | ||
* | ||
* Fill this stub out by replacing all the `any` types. | ||
* | ||
* Once filled out, we encourage you to share your work with the | ||
* community by sending a pull request to: | ||
* https://github.com/flowtype/flow-typed | ||
*/ | ||
|
||
declare module 'react-input-files' { | ||
declare module.exports: any; | ||
} | ||
|
||
/** | ||
* We include stubs for each file inside this npm package in case you need to | ||
* require those files directly. Feel free to delete any files that aren't | ||
* needed. | ||
*/ | ||
declare module 'react-input-files/lib/index' { | ||
declare module.exports: any; | ||
} | ||
|
||
declare module 'react-input-files/lib/InputFiles' { | ||
declare module.exports: any; | ||
} | ||
|
||
// Filename aliases | ||
declare module 'react-input-files/lib/index.js' { | ||
declare module.exports: $Exports<'react-input-files/lib/index'>; | ||
} | ||
declare module 'react-input-files/lib/InputFiles.js' { | ||
declare module.exports: $Exports<'react-input-files/lib/InputFiles'>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// flow-typed signature: a06aad5a31f4e384b859c53728026735 | ||
// flow-typed version: <<STUB>>/round-to_v^3.0.0/flow_v0.87.0 | ||
|
||
/** | ||
* This is an autogenerated libdef stub for: | ||
* | ||
* 'round-to' | ||
* | ||
* Fill this stub out by replacing all the `any` types. | ||
* | ||
* Once filled out, we encourage you to share your work with the | ||
* community by sending a pull request to: | ||
* https://github.com/flowtype/flow-typed | ||
*/ | ||
|
||
declare module 'round-to' { | ||
declare module.exports: any; | ||
} | ||
|
||
/** | ||
* We include stubs for each file inside this npm package in case you need to | ||
* require those files directly. Feel free to delete any files that aren't | ||
* needed. | ||
*/ | ||
|
||
|
||
// Filename aliases | ||
declare module 'round-to/index' { | ||
declare module.exports: $Exports<'round-to'>; | ||
} | ||
declare module 'round-to/index.js' { | ||
declare module.exports: $Exports<'round-to'>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// @flow | ||
import * as React from 'react'; | ||
import { storiesOf } from '@storybook/react'; | ||
import { action } from '@storybook/addon-actions'; | ||
import FileDropzone from '.'; | ||
|
||
class StatefulFileDropzone extends React.PureComponent<*, { file: ?File }> { | ||
state = { file: null }; | ||
|
||
componentWillUnmount() { | ||
if (this.timeoutID) clearTimeout(this.timeoutID); | ||
} | ||
|
||
onFileChange = (file, setFileUploading) => { | ||
this.setState({ file }); | ||
action('onFileChange')(file); | ||
|
||
setFileUploading(true); | ||
this.timeoutID = setTimeout(() => { | ||
setFileUploading(false); | ||
}, 2000); | ||
}; | ||
|
||
timeoutID: TimeoutID; | ||
|
||
render() { | ||
const { file } = this.state; | ||
const { onFileChange } = this; | ||
|
||
return <FileDropzone value={file} onFileChange={onFileChange} />; | ||
} | ||
} | ||
|
||
storiesOf('FileDropzone', module) | ||
.add( | ||
'API', | ||
() => <FileDropzone value={null} onFileChange={action('onFileChange')} />, | ||
{ | ||
info: { | ||
text: 'default', | ||
inline: true, | ||
}, | ||
}, | ||
) | ||
.add( | ||
'With custom text', | ||
() => ( | ||
<FileDropzone | ||
value={null} | ||
onFileChange={action('onFileChange')} | ||
placeholder="請拖拉檔案至此,或者您可以點擊" | ||
browse="瀏覽" | ||
dot="。" | ||
accept="*" | ||
/> | ||
), | ||
{ | ||
info: { | ||
text: 'default', | ||
inline: true, | ||
}, | ||
}, | ||
) | ||
.add( | ||
'With file value', | ||
() => ( | ||
<FileDropzone | ||
// $FlowFixMe | ||
value={{ | ||
name: 'text.txt', | ||
size: 12000, | ||
}} | ||
onFileChange={action('onFileChange')} | ||
/> | ||
), | ||
{ | ||
info: { | ||
text: 'default', | ||
inline: true, | ||
}, | ||
}, | ||
) | ||
.add('With state', () => <StatefulFileDropzone />, { | ||
info: { | ||
text: 'default', | ||
inline: true, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// @flow | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { compose, pure } from 'recompose'; | ||
import Dropzone from 'react-dnd-dropzone'; | ||
import InputFiles from 'react-input-files'; | ||
import Spin from '../Spin'; | ||
import A from '../A'; | ||
import P from '../P'; | ||
import { IconLoading, IconDone, IconFileOutline } from '../Icons'; | ||
import withSubmittingState, { | ||
type InjectedProps as WithSubmittingStateInjectedProps, | ||
} from '../HoCs/withSubmittingState'; | ||
import { type SetSubmitting } from '../utils/type.flow'; | ||
import toMB from '../utils/toMB'; | ||
import { | ||
Container, | ||
FileNameDisplay, | ||
FileLoadingIcon, | ||
TextOverflow, | ||
} from './styled-components'; | ||
|
||
export type Props = { | ||
value: ?File, | ||
onFileChange: (File, setSubmitting: SetSubmitting) => void | Promise<void>, | ||
placeholder?: string, | ||
browse?: string, | ||
dot?: string, | ||
accept?: string, | ||
}; | ||
type InnerProps = Props & WithSubmittingStateInjectedProps; | ||
|
||
export class PureFileDropzone extends React.PureComponent<InnerProps> { | ||
static propTypes = { | ||
value: PropTypes.shape({ | ||
name: PropTypes.string, | ||
size: PropTypes.number, | ||
}), | ||
onFileChange: PropTypes.func.isRequired, // (File, setSubmitting: SetSubmitting) => void | Promise<void> | ||
placeholder: PropTypes.string, | ||
browse: PropTypes.string, | ||
dot: PropTypes.string, | ||
accept: PropTypes.string, | ||
}; | ||
|
||
static defaultProps = { | ||
placeholder: 'Drag & drop your file here or ', | ||
browse: 'browse', | ||
dot: '.', | ||
accept: '*', | ||
}; | ||
|
||
onFileChange = async ([file]: Array<File>) => { | ||
const { onFileChange, setSubmitting } = this.props; | ||
try { | ||
await onFileChange(file, setSubmitting); | ||
} catch (error) { | ||
throw new Error(error); | ||
} | ||
}; | ||
|
||
render() { | ||
const { | ||
value, | ||
isSubmitting, | ||
placeholder, | ||
browse, | ||
dot, | ||
accept, | ||
} = this.props; | ||
const { onFileChange } = this; | ||
|
||
return ( | ||
<React.Fragment> | ||
<Dropzone | ||
onDrop={onFileChange} | ||
render={({ canDrop }) => ( | ||
<Container canDrop={canDrop}> | ||
<div> | ||
{placeholder} | ||
<InputFiles onChange={onFileChange} accept={accept}> | ||
<A>{browse}</A> | ||
</InputFiles> | ||
{dot} | ||
</div> | ||
</Container> | ||
)} | ||
/> | ||
|
||
{value && ( | ||
<FileNameDisplay> | ||
<div> | ||
<IconFileOutline width={18} height={18} /> | ||
<TextOverflow>{value.name}</TextOverflow> | ||
</div> | ||
<div> | ||
<P color="grayDark">{toMB(value.size)}</P> | ||
<FileLoadingIcon isLoading={isSubmitting}> | ||
{isSubmitting ? ( | ||
<Spin> | ||
<IconLoading width={18} height={18} /> | ||
</Spin> | ||
) : ( | ||
<IconDone width={18} height={18} /> | ||
)} | ||
</FileLoadingIcon> | ||
</div> | ||
</FileNameDisplay> | ||
)} | ||
</React.Fragment> | ||
); | ||
} | ||
} | ||
|
||
const enhance = compose( | ||
pure, | ||
withSubmittingState, | ||
); | ||
const FileDropzone: React.ComponentType<Props> = enhance(PureFileDropzone); | ||
FileDropzone.displayName = 'FileDropzone'; | ||
|
||
export default FileDropzone; |
Oops, something went wrong.