Revert "Add yet-another-react-lightbox package and update .gitignore to exclude node_modules"
This reverts commit c92f4a5edd.
This commit is contained in:
28
frontend/node_modules/react-dropzone/.babelrc.js
generated
vendored
Normal file
28
frontend/node_modules/react-dropzone/.babelrc.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// https://babeljs.io/docs/en/configuration
|
||||
const presets = ['@babel/preset-react']
|
||||
if (process.env['BABEL_ENV'] === 'es') {
|
||||
presets.unshift(['@babel/preset-env', { modules: false }])
|
||||
} else {
|
||||
presets.unshift('@babel/preset-env')
|
||||
}
|
||||
|
||||
const plugins = [
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
'@babel/plugin-proposal-logical-assignment-operators',
|
||||
['@babel/plugin-proposal-optional-chaining', { loose: false }],
|
||||
['@babel/plugin-proposal-pipeline-operator', { proposal: 'minimal' }],
|
||||
['@babel/plugin-proposal-nullish-coalescing-operator', { loose: false }],
|
||||
'@babel/plugin-proposal-do-expressions',
|
||||
'add-module-exports'
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
presets,
|
||||
plugins,
|
||||
env: {
|
||||
test: {
|
||||
presets,
|
||||
plugins: [...plugins, '@babel/plugin-transform-runtime', 'dynamic-import-node']
|
||||
}
|
||||
}
|
||||
}
|
||||
15
frontend/node_modules/react-dropzone/.codeclimate.yml
generated
vendored
Normal file
15
frontend/node_modules/react-dropzone/.codeclimate.yml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
engines:
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- javascript
|
||||
eslint:
|
||||
enabled: true
|
||||
channel: "eslint-3"
|
||||
ratings:
|
||||
paths:
|
||||
- "**.js"
|
||||
exclude_paths:
|
||||
- "dist/"
|
||||
- "src/*.spec.js"
|
||||
13
frontend/node_modules/react-dropzone/.editorconfig
generated
vendored
Normal file
13
frontend/node_modules/react-dropzone/.editorconfig
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
3
frontend/node_modules/react-dropzone/.eslintignore
generated
vendored
Normal file
3
frontend/node_modules/react-dropzone/.eslintignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
coverage/
|
||||
dist/
|
||||
styleguide/*
|
||||
37
frontend/node_modules/react-dropzone/.eslintrc
generated
vendored
Normal file
37
frontend/node_modules/react-dropzone/.eslintrc
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"plugins": [
|
||||
"import",
|
||||
"node",
|
||||
"prettier"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"strict": 0,
|
||||
"node/no-unpublished-require": 0,
|
||||
// Import
|
||||
"import/no-extraneous-dependencies": [
|
||||
2,
|
||||
{
|
||||
"devDependencies": [
|
||||
"webpack*.js",
|
||||
"**/*.spec.js",
|
||||
"**/*.config.js",
|
||||
"**/testSetup.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
6
frontend/node_modules/react-dropzone/.gitpod.yml
generated
vendored
Normal file
6
frontend/node_modules/react-dropzone/.gitpod.yml
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
tasks:
|
||||
- init: yarn install && yarn run build
|
||||
command: yarn run start
|
||||
ports:
|
||||
- port: 8080
|
||||
onOpen: open-preview
|
||||
5
frontend/node_modules/react-dropzone/.husky/commit-msg
generated
vendored
Executable file
5
frontend/node_modules/react-dropzone/.husky/commit-msg
generated
vendored
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# Check commit msg format
|
||||
yarn commitlint --edit $1
|
||||
5
frontend/node_modules/react-dropzone/.husky/pre-commit
generated
vendored
Executable file
5
frontend/node_modules/react-dropzone/.husky/pre-commit
generated
vendored
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# Check whatever is staged for issues
|
||||
yarn lint-staged
|
||||
1
frontend/node_modules/react-dropzone/.nvmrc
generated
vendored
Normal file
1
frontend/node_modules/react-dropzone/.nvmrc
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
20.17.0
|
||||
22
frontend/node_modules/react-dropzone/LICENSE
generated
vendored
Normal file
22
frontend/node_modules/react-dropzone/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Param Aggarwal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
444
frontend/node_modules/react-dropzone/README.md
generated
vendored
Normal file
444
frontend/node_modules/react-dropzone/README.md
generated
vendored
Normal file
@@ -0,0 +1,444 @@
|
||||

|
||||
|
||||
# react-dropzone
|
||||
[](https://www.npmjs.com/package/react-dropzone)
|
||||

|
||||
[](https://codecov.io/gh/react-dropzone/react-dropzone)
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
[](https://gitpod.io/#https://github.com/react-dropzone/react-dropzone)
|
||||
[](https://github.com/react-dropzone/.github/blob/main/CODE_OF_CONDUCT.md)
|
||||
|
||||
Simple React hook to create a HTML5-compliant drag'n'drop zone for files.
|
||||
|
||||
Documentation and examples at https://react-dropzone.js.org. Source code at https://github.com/react-dropzone/react-dropzone/.
|
||||
|
||||
|
||||
## Installation
|
||||
Install it from npm and include it in your React build process (using [Webpack](http://webpack.github.io/), [Browserify](http://browserify.org/), etc).
|
||||
|
||||
```bash
|
||||
npm install --save react-dropzone
|
||||
```
|
||||
or:
|
||||
```bash
|
||||
yarn add react-dropzone
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
You can either use the hook:
|
||||
|
||||
```jsx static
|
||||
import React, {useCallback} from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
|
||||
function MyDropzone() {
|
||||
const onDrop = useCallback(acceptedFiles => {
|
||||
// Do something with the files
|
||||
}, [])
|
||||
const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
|
||||
|
||||
return (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
{
|
||||
isDragActive ?
|
||||
<p>Drop the files here ...</p> :
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Or the wrapper component for the hook:
|
||||
```jsx static
|
||||
import React from 'react'
|
||||
import Dropzone from 'react-dropzone'
|
||||
|
||||
<Dropzone onDrop={acceptedFiles => console.log(acceptedFiles)}>
|
||||
{({getRootProps, getInputProps}) => (
|
||||
<section>
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</Dropzone>
|
||||
```
|
||||
|
||||
If you want to access file contents you have to use the [FileReader API](https://developer.mozilla.org/en-US/docs/Web/API/FileReader):
|
||||
|
||||
```jsx static
|
||||
import React, {useCallback} from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
|
||||
function MyDropzone() {
|
||||
const onDrop = useCallback((acceptedFiles) => {
|
||||
acceptedFiles.forEach((file) => {
|
||||
const reader = new FileReader()
|
||||
|
||||
reader.onabort = () => console.log('file reading was aborted')
|
||||
reader.onerror = () => console.log('file reading has failed')
|
||||
reader.onload = () => {
|
||||
// Do whatever you want with the file contents
|
||||
const binaryStr = reader.result
|
||||
console.log(binaryStr)
|
||||
}
|
||||
reader.readAsArrayBuffer(file)
|
||||
})
|
||||
|
||||
}, [])
|
||||
const {getRootProps, getInputProps} = useDropzone({onDrop})
|
||||
|
||||
return (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Dropzone Props Getters
|
||||
The dropzone property getters are just two functions that return objects with properties which you need to use to create the drag 'n' drop zone.
|
||||
The root properties can be applied to whatever element you want, whereas the input properties must be applied to an `<input>`:
|
||||
```jsx static
|
||||
import React from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
|
||||
function MyDropzone() {
|
||||
const {getRootProps, getInputProps} = useDropzone()
|
||||
|
||||
return (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Note that whatever other props you want to add to the element where the props from `getRootProps()` are set, you should always pass them through that function rather than applying them on the element itself.
|
||||
This is in order to avoid your props being overridden (or overriding the props returned by `getRootProps()`):
|
||||
```jsx static
|
||||
<div
|
||||
{...getRootProps({
|
||||
onClick: event => console.log(event),
|
||||
role: 'button',
|
||||
'aria-label': 'drag and drop area',
|
||||
...
|
||||
})}
|
||||
/>
|
||||
```
|
||||
|
||||
In the example above, the provided `{onClick}` handler will be invoked before the internal one, therefore, internal callbacks can be prevented by simply using [stopPropagation](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation).
|
||||
See [Events](https://react-dropzone.js.org#events) for more examples.
|
||||
|
||||
*Important*: if you omit rendering an `<input>` and/or binding the props from `getInputProps()`, opening a file dialog will not be possible.
|
||||
|
||||
## Refs
|
||||
Both `getRootProps` and `getInputProps` accept a custom `refKey` (defaults to `ref`) as one of the attributes passed down in the parameter.
|
||||
|
||||
This can be useful when the element you're trying to apply the props from either one of those fns does not expose a reference to the element, e.g:
|
||||
|
||||
```jsx static
|
||||
import React from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
// NOTE: After v4.0.0, styled components exposes a ref using forwardRef,
|
||||
// therefore, no need for using innerRef as refKey
|
||||
import styled from 'styled-components'
|
||||
|
||||
const StyledDiv = styled.div`
|
||||
// Some styling here
|
||||
`
|
||||
function Example() {
|
||||
const {getRootProps, getInputProps} = useDropzone()
|
||||
<StyledDiv {...getRootProps({ refKey: 'innerRef' })}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</StyledDiv>
|
||||
}
|
||||
```
|
||||
|
||||
If you're working with [Material UI v4](https://v4.mui.com/) and would like to apply the root props on some component that does not expose a ref, use [RootRef](https://v4.mui.com/api/root-ref/):
|
||||
|
||||
```jsx static
|
||||
import React from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
import RootRef from '@material-ui/core/RootRef'
|
||||
|
||||
function PaperDropzone() {
|
||||
const {getRootProps, getInputProps} = useDropzone()
|
||||
const {ref, ...rootProps} = getRootProps()
|
||||
|
||||
<RootRef rootRef={ref}>
|
||||
<Paper {...rootProps}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</Paper>
|
||||
</RootRef>
|
||||
}
|
||||
```
|
||||
|
||||
**IMPORTANT**: do not set the `ref` prop on the elements where `getRootProps()`/`getInputProps()` props are set, instead, get the refs from the hook itself:
|
||||
|
||||
```jsx static
|
||||
import React from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
|
||||
function Refs() {
|
||||
const {
|
||||
getRootProps,
|
||||
getInputProps,
|
||||
rootRef, // Ref to the `<div>`
|
||||
inputRef // Ref to the `<input>`
|
||||
} = useDropzone()
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
}
|
||||
```
|
||||
|
||||
If you're using the `<Dropzone>` component, though, you can set the `ref` prop on the component itself which will expose the `{open}` prop that can be used to open the file dialog programmatically:
|
||||
|
||||
```jsx static
|
||||
import React, {createRef} from 'react'
|
||||
import Dropzone from 'react-dropzone'
|
||||
|
||||
const dropzoneRef = createRef()
|
||||
|
||||
<Dropzone ref={dropzoneRef}>
|
||||
{({getRootProps, getInputProps}) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
|
||||
dropzoneRef.open()
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
`react-dropzone` makes some of its drag 'n' drop callbacks asynchronous to enable promise based `getFilesFromEvent()` functions. In order to test components that use this library, you need to use the [react-testing-library](https://github.com/testing-library/react-testing-library):
|
||||
```js static
|
||||
import React from 'react'
|
||||
import Dropzone from 'react-dropzone'
|
||||
import {act, fireEvent, render} from '@testing-library/react'
|
||||
|
||||
test('invoke onDragEnter when dragenter event occurs', async () => {
|
||||
const file = new File([
|
||||
JSON.stringify({ping: true})
|
||||
], 'ping.json', { type: 'application/json' })
|
||||
const data = mockData([file])
|
||||
const onDragEnter = jest.fn()
|
||||
|
||||
const ui = (
|
||||
<Dropzone onDragEnter={onDragEnter}>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
)
|
||||
const { container } = render(ui)
|
||||
|
||||
await act(
|
||||
() => fireEvent.dragEnter(
|
||||
container.querySelector('div'),
|
||||
data,
|
||||
)
|
||||
);
|
||||
expect(onDragEnter).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
function mockData(files) {
|
||||
return {
|
||||
dataTransfer: {
|
||||
files,
|
||||
items: files.map(file => ({
|
||||
kind: 'file',
|
||||
type: file.type,
|
||||
getAsFile: () => file
|
||||
})),
|
||||
types: ['Files']
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE**: using [Enzyme](https://airbnb.io/enzyme) for testing is not supported at the moment, see [#2011](https://github.com/airbnb/enzyme/issues/2011).
|
||||
|
||||
More examples for this can be found in `react-dropzone`'s own [test suites](https://github.com/react-dropzone/react-dropzone/blob/master/src/index.spec.js).
|
||||
|
||||
## Caveats
|
||||
### Required React Version
|
||||
React [16.8](https://reactjs.org/blog/2019/02/06/react-v16.8.0.html) or above is required because we use [hooks](https://reactjs.org/docs/hooks-intro.html) (the lib itself is a hook).
|
||||
|
||||
### File Paths
|
||||
Files returned by the hook or passed as arg to the `onDrop` cb won't have the properties `path` or `fullPath`.
|
||||
For more inf check [this SO question](https://stackoverflow.com/a/23005925/2275818) and [this issue](https://github.com/react-dropzone/react-dropzone/issues/477).
|
||||
|
||||
### Not a File Uploader
|
||||
This lib is not a file uploader; as such, it does not process files or provide any way to make HTTP requests to some server; if you're looking for that, checkout [filepond](https://pqina.nl/filepond) or [uppy.io](https://uppy.io/).
|
||||
|
||||
### Using \<label\> as Root
|
||||
If you use [\<label\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) as the root element, the file dialog will be opened twice; see [#1107](https://github.com/react-dropzone/react-dropzone/issues/1107) why. To avoid this, use `noClick`:
|
||||
```jsx static
|
||||
import React, {useCallback} from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
|
||||
function MyDropzone() {
|
||||
const {getRootProps, getInputProps} = useDropzone({noClick: true})
|
||||
|
||||
return (
|
||||
<label {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
</label>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Using open() on Click
|
||||
If you bind a click event on an inner element and use `open()`, it will trigger a click on the root element too, resulting in the file dialog opening twice. To prevent this, use the `noClick` on the root:
|
||||
```jsx static
|
||||
import React, {useCallback} from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
|
||||
function MyDropzone() {
|
||||
const {getRootProps, getInputProps, open} = useDropzone({noClick: true})
|
||||
|
||||
return (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<button type="button" onClick={open}>
|
||||
Open
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### File Dialog Cancel Callback
|
||||
The `onFileDialogCancel()` cb is unstable in most browsers, meaning, there's a good chance of it being triggered even though you have selected files.
|
||||
|
||||
We rely on using a timeout of `300ms` after the window is focused (the window `onfocus` event is triggered when the file select dialog is closed) to check if any files were selected and trigger `onFileDialogCancel` if none were selected.
|
||||
|
||||
As one can imagine, this doesn't really work if there's a lot of files or large files as by the time we trigger the check, the browser is still processing the files and no `onchange` events are triggered yet on the input. Check [#1031](https://github.com/react-dropzone/react-dropzone/issues/1031) for more info.
|
||||
|
||||
Fortunately, there's the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API), which is currently a working draft and some browsers support it (see [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker#browser_compatibility)), that provides a reliable way to prompt the user for file selection and capture cancellation.
|
||||
|
||||
Also keep in mind that the FS access API can only be used in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
|
||||
|
||||
**NOTE** You can enable using the FS access API with the `useFsAccessApi` property: `useDropzone({useFsAccessApi: true})`.
|
||||
|
||||
### File System Access API
|
||||
|
||||
When setting `useFsAccessApi` to `true`, you're switching to the [File System API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API) (see the [file system access](https://wicg.github.io/file-system-access/) RFC).
|
||||
|
||||
What this essentially does is that it will use the [showOpenFilePicker](https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker) method to open the file picker window so that the user can select files.
|
||||
|
||||
In contrast, the traditional way (when the `useFsAccessApi` is not set to `true` or not specified) uses an `<input type="file">` (see [docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file)) on which a click event is triggered.
|
||||
|
||||
With the use of the file system access API enabled, there's a couple of caveats to keep in mind:
|
||||
1. The users will not be able to select directories
|
||||
2. It requires the app to run in a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)
|
||||
3. In [Electron](https://www.electronjs.org/), the path may not be set (see [#1249](https://github.com/react-dropzone/react-dropzone/issues/1249))
|
||||
|
||||
## Supported Browsers
|
||||
We use [browserslist](https://github.com/browserslist/browserslist) config to state the browser support for this lib, so check it out on [browserslist.dev](https://browserslist.dev/?q=ZGVmYXVsdHM%3D).
|
||||
|
||||
|
||||
## Need image editing?
|
||||
React Dropzone integrates perfectly with [Pintura Image Editor](https://pqina.nl/pintura/?ref=react-dropzone), creating a modern image editing experience. Pintura supports crop aspect ratios, resizing, rotating, cropping, annotating, filtering, and much more.
|
||||
|
||||
Checkout the [Pintura integration example](https://codesandbox.io/s/react-dropzone-pintura-40xh4?file=/src/App.js).
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
### Backers
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/react-dropzone#backer)]
|
||||
|
||||
<a href="https://opencollective.com/react-dropzone/backer/0/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/1/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/2/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/3/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/4/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/5/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/6/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/7/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/8/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/9/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/10/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/11/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/12/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/13/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/14/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/15/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/16/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/17/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/18/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/19/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/20/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/21/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/22/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/23/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/24/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/25/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/26/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/27/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/28/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/backer/29/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/29/avatar.svg"></a>
|
||||
|
||||
|
||||
### Sponsors
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/react-dropzone#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/0/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/1/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/2/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/3/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/4/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/5/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/6/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/7/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/8/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/9/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/10/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/11/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/12/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/13/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/14/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/15/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/16/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/17/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/18/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/19/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/20/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/21/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/22/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/23/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/24/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/25/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/26/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/27/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/28/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/react-dropzone/sponsor/29/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/29/avatar.svg"></a>
|
||||
|
||||
|
||||
### Hosting
|
||||
[react-dropzone.js.org](https://react-dropzone.js.org/) hosting provided by [netlify](https://www.netlify.com/).
|
||||
|
||||
## Contribute
|
||||
Checkout the organization [CONTRIBUTING.md](https://github.com/react-dropzone/.github/blob/main/CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
MIT
|
||||
1
frontend/node_modules/react-dropzone/commitlint.config.js
generated
vendored
Normal file
1
frontend/node_modules/react-dropzone/commitlint.config.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = { extends: ["@commitlint/config-angular"] };
|
||||
983
frontend/node_modules/react-dropzone/dist/es/index.js
generated
vendored
Executable file
983
frontend/node_modules/react-dropzone/dist/es/index.js
generated
vendored
Executable file
@@ -0,0 +1,983 @@
|
||||
var _excluded = ["children"],
|
||||
_excluded2 = ["open"],
|
||||
_excluded3 = ["refKey", "role", "onKeyDown", "onFocus", "onBlur", "onClick", "onDragEnter", "onDragOver", "onDragLeave", "onDrop"],
|
||||
_excluded4 = ["refKey", "onChange", "onClick"];
|
||||
|
||||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
||||
|
||||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
||||
|
||||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
||||
|
||||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
||||
|
||||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
||||
|
||||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
||||
|
||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
||||
|
||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
||||
|
||||
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
||||
|
||||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
||||
|
||||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
||||
|
||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
||||
|
||||
/* eslint prefer-template: 0 */
|
||||
import React, { forwardRef, Fragment, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { fromEvent } from "file-selector";
|
||||
import { acceptPropAsAcceptAttr, allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, canUseFileSystemAccessAPI, isAbort, isEvtWithFiles, isIeOrEdge, isPropagationStopped, isSecurityError, onDocumentDragOver, pickerOptionsFromAccept, TOO_MANY_FILES_REJECTION } from "./utils/index";
|
||||
/**
|
||||
* Convenience wrapper component for the `useDropzone` hook
|
||||
*
|
||||
* ```jsx
|
||||
* <Dropzone>
|
||||
* {({getRootProps, getInputProps}) => (
|
||||
* <div {...getRootProps()}>
|
||||
* <input {...getInputProps()} />
|
||||
* <p>Drag 'n' drop some files here, or click to select files</p>
|
||||
* </div>
|
||||
* )}
|
||||
* </Dropzone>
|
||||
* ```
|
||||
*/
|
||||
|
||||
var Dropzone = /*#__PURE__*/forwardRef(function (_ref, ref) {
|
||||
var children = _ref.children,
|
||||
params = _objectWithoutProperties(_ref, _excluded);
|
||||
|
||||
var _useDropzone = useDropzone(params),
|
||||
open = _useDropzone.open,
|
||||
props = _objectWithoutProperties(_useDropzone, _excluded2);
|
||||
|
||||
useImperativeHandle(ref, function () {
|
||||
return {
|
||||
open: open
|
||||
};
|
||||
}, [open]); // TODO: Figure out why react-styleguidist cannot create docs if we don't return a jsx element
|
||||
|
||||
return /*#__PURE__*/React.createElement(Fragment, null, children(_objectSpread(_objectSpread({}, props), {}, {
|
||||
open: open
|
||||
})));
|
||||
});
|
||||
Dropzone.displayName = "Dropzone"; // Add default props for react-docgen
|
||||
|
||||
var defaultProps = {
|
||||
disabled: false,
|
||||
getFilesFromEvent: fromEvent,
|
||||
maxSize: Infinity,
|
||||
minSize: 0,
|
||||
multiple: true,
|
||||
maxFiles: 0,
|
||||
preventDropOnDocument: true,
|
||||
noClick: false,
|
||||
noKeyboard: false,
|
||||
noDrag: false,
|
||||
noDragEventsBubbling: false,
|
||||
validator: null,
|
||||
useFsAccessApi: false,
|
||||
autoFocus: false
|
||||
};
|
||||
Dropzone.defaultProps = defaultProps;
|
||||
Dropzone.propTypes = {
|
||||
/**
|
||||
* Render function that exposes the dropzone state and prop getter fns
|
||||
*
|
||||
* @param {object} params
|
||||
* @param {Function} params.getRootProps Returns the props you should apply to the root drop container you render
|
||||
* @param {Function} params.getInputProps Returns the props you should apply to hidden file input you render
|
||||
* @param {Function} params.open Open the native file selection dialog
|
||||
* @param {boolean} params.isFocused Dropzone area is in focus
|
||||
* @param {boolean} params.isFileDialogActive File dialog is opened
|
||||
* @param {boolean} params.isDragActive Active drag is in progress
|
||||
* @param {boolean} params.isDragAccept Dragged files are accepted
|
||||
* @param {boolean} params.isDragReject Some dragged files are rejected
|
||||
* @param {File[]} params.acceptedFiles Accepted files
|
||||
* @param {FileRejection[]} params.fileRejections Rejected files and why they were rejected
|
||||
*/
|
||||
children: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Set accepted file types.
|
||||
* Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
|
||||
* Keep in mind that mime type determination is not reliable across platforms. CSV files,
|
||||
* for example, are reported as text/plain under macOS but as application/vnd.ms-excel under
|
||||
* Windows. In some cases there might not be a mime type set at all (https://github.com/react-dropzone/react-dropzone/issues/276).
|
||||
*/
|
||||
accept: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.string)),
|
||||
|
||||
/**
|
||||
* Allow drag 'n' drop (or selection from the file dialog) of multiple files
|
||||
*/
|
||||
multiple: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* If false, allow dropped items to take over the current browser window
|
||||
*/
|
||||
preventDropOnDocument: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* If true, disables click to open the native file selection dialog
|
||||
*/
|
||||
noClick: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* If true, disables SPACE/ENTER to open the native file selection dialog.
|
||||
* Note that it also stops tracking the focus state.
|
||||
*/
|
||||
noKeyboard: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* If true, disables drag 'n' drop
|
||||
*/
|
||||
noDrag: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* If true, stops drag event propagation to parents
|
||||
*/
|
||||
noDragEventsBubbling: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Minimum file size (in bytes)
|
||||
*/
|
||||
minSize: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Maximum file size (in bytes)
|
||||
*/
|
||||
maxSize: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Maximum accepted number of files
|
||||
* The default value is 0 which means there is no limitation to how many files are accepted.
|
||||
*/
|
||||
maxFiles: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Enable/disable the dropzone
|
||||
*/
|
||||
disabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Use this to provide a custom file aggregator
|
||||
*
|
||||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
|
||||
*/
|
||||
getFilesFromEvent: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Cb for when closing the file dialog with no selection
|
||||
*/
|
||||
onFileDialogCancel: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Cb for when opening the file dialog
|
||||
*/
|
||||
onFileDialogOpen: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
|
||||
* to open the file picker instead of using an `<input type="file">` click event.
|
||||
*/
|
||||
useFsAccessApi: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Set to true to focus the root element on render
|
||||
*/
|
||||
autoFocus: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Cb for when the `dragenter` event occurs.
|
||||
*
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
onDragEnter: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Cb for when the `dragleave` event occurs
|
||||
*
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
onDragLeave: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Cb for when the `dragover` event occurs
|
||||
*
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
onDragOver: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Cb for when the `drop` event occurs.
|
||||
* Note that this callback is invoked after the `getFilesFromEvent` callback is done.
|
||||
*
|
||||
* Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and `maxSize` props.
|
||||
* `accept` must be a valid [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml) according to [input element specification](https://www.w3.org/wiki/HTML/Elements/input/file) or a valid file extension.
|
||||
* If `multiple` is set to false and additional files are dropped,
|
||||
* all files besides the first will be rejected.
|
||||
* Any file which does not have a size in the [`minSize`, `maxSize`] range, will be rejected as well.
|
||||
*
|
||||
* Note that the `onDrop` callback will always be invoked regardless if the dropped files were accepted or rejected.
|
||||
* If you'd like to react to a specific scenario, use the `onDropAccepted`/`onDropRejected` props.
|
||||
*
|
||||
* `onDrop` will provide you with an array of [File](https://developer.mozilla.org/en-US/docs/Web/API/File) objects which you can then process and send to a server.
|
||||
* For example, with [SuperAgent](https://github.com/visionmedia/superagent) as a http/ajax library:
|
||||
*
|
||||
* ```js
|
||||
* function onDrop(acceptedFiles) {
|
||||
* const req = request.post('/upload')
|
||||
* acceptedFiles.forEach(file => {
|
||||
* req.attach(file.name, file)
|
||||
* })
|
||||
* req.end(callback)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {File[]} acceptedFiles
|
||||
* @param {FileRejection[]} fileRejections
|
||||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
|
||||
*/
|
||||
onDrop: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Cb for when the `drop` event occurs.
|
||||
* Note that if no files are accepted, this callback is not invoked.
|
||||
*
|
||||
* @param {File[]} files
|
||||
* @param {(DragEvent|Event)} event
|
||||
*/
|
||||
onDropAccepted: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Cb for when the `drop` event occurs.
|
||||
* Note that if no files are rejected, this callback is not invoked.
|
||||
*
|
||||
* @param {FileRejection[]} fileRejections
|
||||
* @param {(DragEvent|Event)} event
|
||||
*/
|
||||
onDropRejected: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Cb for when there's some error from any of the promises.
|
||||
*
|
||||
* @param {Error} error
|
||||
*/
|
||||
onError: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Custom validation function. It must return null if there's no errors.
|
||||
* @param {File} file
|
||||
* @returns {FileError|FileError[]|null}
|
||||
*/
|
||||
validator: PropTypes.func
|
||||
};
|
||||
export default Dropzone;
|
||||
/**
|
||||
* A function that is invoked for the `dragenter`,
|
||||
* `dragover` and `dragleave` events.
|
||||
* It is not invoked if the items are not files (such as link, text, etc.).
|
||||
*
|
||||
* @callback dragCb
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
|
||||
/**
|
||||
* A function that is invoked for the `drop` or input change event.
|
||||
* It is not invoked if the items are not files (such as link, text, etc.).
|
||||
*
|
||||
* @callback dropCb
|
||||
* @param {File[]} acceptedFiles List of accepted files
|
||||
* @param {FileRejection[]} fileRejections List of rejected files and why they were rejected
|
||||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
|
||||
*/
|
||||
|
||||
/**
|
||||
* A function that is invoked for the `drop` or input change event.
|
||||
* It is not invoked if the items are files (such as link, text, etc.).
|
||||
*
|
||||
* @callback dropAcceptedCb
|
||||
* @param {File[]} files List of accepted files that meet the given criteria
|
||||
* (`accept`, `multiple`, `minSize`, `maxSize`)
|
||||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
|
||||
*/
|
||||
|
||||
/**
|
||||
* A function that is invoked for the `drop` or input change event.
|
||||
*
|
||||
* @callback dropRejectedCb
|
||||
* @param {File[]} files List of rejected files that do not meet the given criteria
|
||||
* (`accept`, `multiple`, `minSize`, `maxSize`)
|
||||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
|
||||
*/
|
||||
|
||||
/**
|
||||
* A function that is used aggregate files,
|
||||
* in a asynchronous fashion, from drag or input change events.
|
||||
*
|
||||
* @callback getFilesFromEvent
|
||||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
|
||||
* @returns {(File[]|Promise<File[]>)}
|
||||
*/
|
||||
|
||||
/**
|
||||
* An object with the current dropzone state.
|
||||
*
|
||||
* @typedef {object} DropzoneState
|
||||
* @property {boolean} isFocused Dropzone area is in focus
|
||||
* @property {boolean} isFileDialogActive File dialog is opened
|
||||
* @property {boolean} isDragActive Active drag is in progress
|
||||
* @property {boolean} isDragAccept Dragged files are accepted
|
||||
* @property {boolean} isDragReject Some dragged files are rejected
|
||||
* @property {File[]} acceptedFiles Accepted files
|
||||
* @property {FileRejection[]} fileRejections Rejected files and why they were rejected
|
||||
*/
|
||||
|
||||
/**
|
||||
* An object with the dropzone methods.
|
||||
*
|
||||
* @typedef {object} DropzoneMethods
|
||||
* @property {Function} getRootProps Returns the props you should apply to the root drop container you render
|
||||
* @property {Function} getInputProps Returns the props you should apply to hidden file input you render
|
||||
* @property {Function} open Open the native file selection dialog
|
||||
*/
|
||||
|
||||
var initialState = {
|
||||
isFocused: false,
|
||||
isFileDialogActive: false,
|
||||
isDragActive: false,
|
||||
isDragAccept: false,
|
||||
isDragReject: false,
|
||||
acceptedFiles: [],
|
||||
fileRejections: []
|
||||
};
|
||||
/**
|
||||
* A React hook that creates a drag 'n' drop area.
|
||||
*
|
||||
* ```jsx
|
||||
* function MyDropzone(props) {
|
||||
* const {getRootProps, getInputProps} = useDropzone({
|
||||
* onDrop: acceptedFiles => {
|
||||
* // do something with the File objects, e.g. upload to some server
|
||||
* }
|
||||
* });
|
||||
* return (
|
||||
* <div {...getRootProps()}>
|
||||
* <input {...getInputProps()} />
|
||||
* <p>Drag and drop some files here, or click to select files</p>
|
||||
* </div>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @function useDropzone
|
||||
*
|
||||
* @param {object} props
|
||||
* @param {import("./utils").AcceptProp} [props.accept] Set accepted file types.
|
||||
* Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
|
||||
* Keep in mind that mime type determination is not reliable across platforms. CSV files,
|
||||
* for example, are reported as text/plain under macOS but as application/vnd.ms-excel under
|
||||
* Windows. In some cases there might not be a mime type set at all (https://github.com/react-dropzone/react-dropzone/issues/276).
|
||||
* @param {boolean} [props.multiple=true] Allow drag 'n' drop (or selection from the file dialog) of multiple files
|
||||
* @param {boolean} [props.preventDropOnDocument=true] If false, allow dropped items to take over the current browser window
|
||||
* @param {boolean} [props.noClick=false] If true, disables click to open the native file selection dialog
|
||||
* @param {boolean} [props.noKeyboard=false] If true, disables SPACE/ENTER to open the native file selection dialog.
|
||||
* Note that it also stops tracking the focus state.
|
||||
* @param {boolean} [props.noDrag=false] If true, disables drag 'n' drop
|
||||
* @param {boolean} [props.noDragEventsBubbling=false] If true, stops drag event propagation to parents
|
||||
* @param {number} [props.minSize=0] Minimum file size (in bytes)
|
||||
* @param {number} [props.maxSize=Infinity] Maximum file size (in bytes)
|
||||
* @param {boolean} [props.disabled=false] Enable/disable the dropzone
|
||||
* @param {getFilesFromEvent} [props.getFilesFromEvent] Use this to provide a custom file aggregator
|
||||
* @param {Function} [props.onFileDialogCancel] Cb for when closing the file dialog with no selection
|
||||
* @param {boolean} [props.useFsAccessApi] Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
|
||||
* to open the file picker instead of using an `<input type="file">` click event.
|
||||
* @param {boolean} autoFocus Set to true to auto focus the root element.
|
||||
* @param {Function} [props.onFileDialogOpen] Cb for when opening the file dialog
|
||||
* @param {dragCb} [props.onDragEnter] Cb for when the `dragenter` event occurs.
|
||||
* @param {dragCb} [props.onDragLeave] Cb for when the `dragleave` event occurs
|
||||
* @param {dragCb} [props.onDragOver] Cb for when the `dragover` event occurs
|
||||
* @param {dropCb} [props.onDrop] Cb for when the `drop` event occurs.
|
||||
* Note that this callback is invoked after the `getFilesFromEvent` callback is done.
|
||||
*
|
||||
* Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and `maxSize` props.
|
||||
* `accept` must be an object with keys as a valid [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml) according to [input element specification](https://www.w3.org/wiki/HTML/Elements/input/file) and the value an array of file extensions (optional).
|
||||
* If `multiple` is set to false and additional files are dropped,
|
||||
* all files besides the first will be rejected.
|
||||
* Any file which does not have a size in the [`minSize`, `maxSize`] range, will be rejected as well.
|
||||
*
|
||||
* Note that the `onDrop` callback will always be invoked regardless if the dropped files were accepted or rejected.
|
||||
* If you'd like to react to a specific scenario, use the `onDropAccepted`/`onDropRejected` props.
|
||||
*
|
||||
* `onDrop` will provide you with an array of [File](https://developer.mozilla.org/en-US/docs/Web/API/File) objects which you can then process and send to a server.
|
||||
* For example, with [SuperAgent](https://github.com/visionmedia/superagent) as a http/ajax library:
|
||||
*
|
||||
* ```js
|
||||
* function onDrop(acceptedFiles) {
|
||||
* const req = request.post('/upload')
|
||||
* acceptedFiles.forEach(file => {
|
||||
* req.attach(file.name, file)
|
||||
* })
|
||||
* req.end(callback)
|
||||
* }
|
||||
* ```
|
||||
* @param {dropAcceptedCb} [props.onDropAccepted]
|
||||
* @param {dropRejectedCb} [props.onDropRejected]
|
||||
* @param {(error: Error) => void} [props.onError]
|
||||
*
|
||||
* @returns {DropzoneState & DropzoneMethods}
|
||||
*/
|
||||
|
||||
export function useDropzone() {
|
||||
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||||
|
||||
var _defaultProps$props = _objectSpread(_objectSpread({}, defaultProps), props),
|
||||
accept = _defaultProps$props.accept,
|
||||
disabled = _defaultProps$props.disabled,
|
||||
getFilesFromEvent = _defaultProps$props.getFilesFromEvent,
|
||||
maxSize = _defaultProps$props.maxSize,
|
||||
minSize = _defaultProps$props.minSize,
|
||||
multiple = _defaultProps$props.multiple,
|
||||
maxFiles = _defaultProps$props.maxFiles,
|
||||
onDragEnter = _defaultProps$props.onDragEnter,
|
||||
onDragLeave = _defaultProps$props.onDragLeave,
|
||||
onDragOver = _defaultProps$props.onDragOver,
|
||||
onDrop = _defaultProps$props.onDrop,
|
||||
onDropAccepted = _defaultProps$props.onDropAccepted,
|
||||
onDropRejected = _defaultProps$props.onDropRejected,
|
||||
onFileDialogCancel = _defaultProps$props.onFileDialogCancel,
|
||||
onFileDialogOpen = _defaultProps$props.onFileDialogOpen,
|
||||
useFsAccessApi = _defaultProps$props.useFsAccessApi,
|
||||
autoFocus = _defaultProps$props.autoFocus,
|
||||
preventDropOnDocument = _defaultProps$props.preventDropOnDocument,
|
||||
noClick = _defaultProps$props.noClick,
|
||||
noKeyboard = _defaultProps$props.noKeyboard,
|
||||
noDrag = _defaultProps$props.noDrag,
|
||||
noDragEventsBubbling = _defaultProps$props.noDragEventsBubbling,
|
||||
onError = _defaultProps$props.onError,
|
||||
validator = _defaultProps$props.validator;
|
||||
|
||||
var acceptAttr = useMemo(function () {
|
||||
return acceptPropAsAcceptAttr(accept);
|
||||
}, [accept]);
|
||||
var pickerTypes = useMemo(function () {
|
||||
return pickerOptionsFromAccept(accept);
|
||||
}, [accept]);
|
||||
var onFileDialogOpenCb = useMemo(function () {
|
||||
return typeof onFileDialogOpen === "function" ? onFileDialogOpen : noop;
|
||||
}, [onFileDialogOpen]);
|
||||
var onFileDialogCancelCb = useMemo(function () {
|
||||
return typeof onFileDialogCancel === "function" ? onFileDialogCancel : noop;
|
||||
}, [onFileDialogCancel]);
|
||||
/**
|
||||
* @constant
|
||||
* @type {React.MutableRefObject<HTMLElement>}
|
||||
*/
|
||||
|
||||
var rootRef = useRef(null);
|
||||
var inputRef = useRef(null);
|
||||
|
||||
var _useReducer = useReducer(reducer, initialState),
|
||||
_useReducer2 = _slicedToArray(_useReducer, 2),
|
||||
state = _useReducer2[0],
|
||||
dispatch = _useReducer2[1];
|
||||
|
||||
var isFocused = state.isFocused,
|
||||
isFileDialogActive = state.isFileDialogActive;
|
||||
var fsAccessApiWorksRef = useRef(typeof window !== "undefined" && window.isSecureContext && useFsAccessApi && canUseFileSystemAccessAPI()); // Update file dialog active state when the window is focused on
|
||||
|
||||
var onWindowFocus = function onWindowFocus() {
|
||||
// Execute the timeout only if the file dialog is opened in the browser
|
||||
if (!fsAccessApiWorksRef.current && isFileDialogActive) {
|
||||
setTimeout(function () {
|
||||
if (inputRef.current) {
|
||||
var files = inputRef.current.files;
|
||||
|
||||
if (!files.length) {
|
||||
dispatch({
|
||||
type: "closeDialog"
|
||||
});
|
||||
onFileDialogCancelCb();
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(function () {
|
||||
window.addEventListener("focus", onWindowFocus, false);
|
||||
return function () {
|
||||
window.removeEventListener("focus", onWindowFocus, false);
|
||||
};
|
||||
}, [inputRef, isFileDialogActive, onFileDialogCancelCb, fsAccessApiWorksRef]);
|
||||
var dragTargetsRef = useRef([]);
|
||||
|
||||
var onDocumentDrop = function onDocumentDrop(event) {
|
||||
if (rootRef.current && rootRef.current.contains(event.target)) {
|
||||
// If we intercepted an event for our instance, let it propagate down to the instance's onDrop handler
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
dragTargetsRef.current = [];
|
||||
};
|
||||
|
||||
useEffect(function () {
|
||||
if (preventDropOnDocument) {
|
||||
document.addEventListener("dragover", onDocumentDragOver, false);
|
||||
document.addEventListener("drop", onDocumentDrop, false);
|
||||
}
|
||||
|
||||
return function () {
|
||||
if (preventDropOnDocument) {
|
||||
document.removeEventListener("dragover", onDocumentDragOver);
|
||||
document.removeEventListener("drop", onDocumentDrop);
|
||||
}
|
||||
};
|
||||
}, [rootRef, preventDropOnDocument]); // Auto focus the root when autoFocus is true
|
||||
|
||||
useEffect(function () {
|
||||
if (!disabled && autoFocus && rootRef.current) {
|
||||
rootRef.current.focus();
|
||||
}
|
||||
|
||||
return function () {};
|
||||
}, [rootRef, autoFocus, disabled]);
|
||||
var onErrCb = useCallback(function (e) {
|
||||
if (onError) {
|
||||
onError(e);
|
||||
} else {
|
||||
// Let the user know something's gone wrong if they haven't provided the onError cb.
|
||||
console.error(e);
|
||||
}
|
||||
}, [onError]);
|
||||
var onDragEnterCb = useCallback(function (event) {
|
||||
event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done
|
||||
|
||||
event.persist();
|
||||
stopPropagation(event);
|
||||
dragTargetsRef.current = [].concat(_toConsumableArray(dragTargetsRef.current), [event.target]);
|
||||
|
||||
if (isEvtWithFiles(event)) {
|
||||
Promise.resolve(getFilesFromEvent(event)).then(function (files) {
|
||||
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fileCount = files.length;
|
||||
var isDragAccept = fileCount > 0 && allFilesAccepted({
|
||||
files: files,
|
||||
accept: acceptAttr,
|
||||
minSize: minSize,
|
||||
maxSize: maxSize,
|
||||
multiple: multiple,
|
||||
maxFiles: maxFiles,
|
||||
validator: validator
|
||||
});
|
||||
var isDragReject = fileCount > 0 && !isDragAccept;
|
||||
dispatch({
|
||||
isDragAccept: isDragAccept,
|
||||
isDragReject: isDragReject,
|
||||
isDragActive: true,
|
||||
type: "setDraggedFiles"
|
||||
});
|
||||
|
||||
if (onDragEnter) {
|
||||
onDragEnter(event);
|
||||
}
|
||||
}).catch(function (e) {
|
||||
return onErrCb(e);
|
||||
});
|
||||
}
|
||||
}, [getFilesFromEvent, onDragEnter, onErrCb, noDragEventsBubbling, acceptAttr, minSize, maxSize, multiple, maxFiles, validator]);
|
||||
var onDragOverCb = useCallback(function (event) {
|
||||
event.preventDefault();
|
||||
event.persist();
|
||||
stopPropagation(event);
|
||||
var hasFiles = isEvtWithFiles(event);
|
||||
|
||||
if (hasFiles && event.dataTransfer) {
|
||||
try {
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
} catch (_unused) {}
|
||||
/* eslint-disable-line no-empty */
|
||||
|
||||
}
|
||||
|
||||
if (hasFiles && onDragOver) {
|
||||
onDragOver(event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}, [onDragOver, noDragEventsBubbling]);
|
||||
var onDragLeaveCb = useCallback(function (event) {
|
||||
event.preventDefault();
|
||||
event.persist();
|
||||
stopPropagation(event); // Only deactivate once the dropzone and all children have been left
|
||||
|
||||
var targets = dragTargetsRef.current.filter(function (target) {
|
||||
return rootRef.current && rootRef.current.contains(target);
|
||||
}); // Make sure to remove a target present multiple times only once
|
||||
// (Firefox may fire dragenter/dragleave multiple times on the same element)
|
||||
|
||||
var targetIdx = targets.indexOf(event.target);
|
||||
|
||||
if (targetIdx !== -1) {
|
||||
targets.splice(targetIdx, 1);
|
||||
}
|
||||
|
||||
dragTargetsRef.current = targets;
|
||||
|
||||
if (targets.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: "setDraggedFiles",
|
||||
isDragActive: false,
|
||||
isDragAccept: false,
|
||||
isDragReject: false
|
||||
});
|
||||
|
||||
if (isEvtWithFiles(event) && onDragLeave) {
|
||||
onDragLeave(event);
|
||||
}
|
||||
}, [rootRef, onDragLeave, noDragEventsBubbling]);
|
||||
var setFiles = useCallback(function (files, event) {
|
||||
var acceptedFiles = [];
|
||||
var fileRejections = [];
|
||||
files.forEach(function (file) {
|
||||
var _fileAccepted = fileAccepted(file, acceptAttr),
|
||||
_fileAccepted2 = _slicedToArray(_fileAccepted, 2),
|
||||
accepted = _fileAccepted2[0],
|
||||
acceptError = _fileAccepted2[1];
|
||||
|
||||
var _fileMatchSize = fileMatchSize(file, minSize, maxSize),
|
||||
_fileMatchSize2 = _slicedToArray(_fileMatchSize, 2),
|
||||
sizeMatch = _fileMatchSize2[0],
|
||||
sizeError = _fileMatchSize2[1];
|
||||
|
||||
var customErrors = validator ? validator(file) : null;
|
||||
|
||||
if (accepted && sizeMatch && !customErrors) {
|
||||
acceptedFiles.push(file);
|
||||
} else {
|
||||
var errors = [acceptError, sizeError];
|
||||
|
||||
if (customErrors) {
|
||||
errors = errors.concat(customErrors);
|
||||
}
|
||||
|
||||
fileRejections.push({
|
||||
file: file,
|
||||
errors: errors.filter(function (e) {
|
||||
return e;
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!multiple && acceptedFiles.length > 1 || multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) {
|
||||
// Reject everything and empty accepted files
|
||||
acceptedFiles.forEach(function (file) {
|
||||
fileRejections.push({
|
||||
file: file,
|
||||
errors: [TOO_MANY_FILES_REJECTION]
|
||||
});
|
||||
});
|
||||
acceptedFiles.splice(0);
|
||||
}
|
||||
|
||||
dispatch({
|
||||
acceptedFiles: acceptedFiles,
|
||||
fileRejections: fileRejections,
|
||||
isDragReject: fileRejections.length > 0,
|
||||
type: "setFiles"
|
||||
});
|
||||
|
||||
if (onDrop) {
|
||||
onDrop(acceptedFiles, fileRejections, event);
|
||||
}
|
||||
|
||||
if (fileRejections.length > 0 && onDropRejected) {
|
||||
onDropRejected(fileRejections, event);
|
||||
}
|
||||
|
||||
if (acceptedFiles.length > 0 && onDropAccepted) {
|
||||
onDropAccepted(acceptedFiles, event);
|
||||
}
|
||||
}, [dispatch, multiple, acceptAttr, minSize, maxSize, maxFiles, onDrop, onDropAccepted, onDropRejected, validator]);
|
||||
var onDropCb = useCallback(function (event) {
|
||||
event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done
|
||||
|
||||
event.persist();
|
||||
stopPropagation(event);
|
||||
dragTargetsRef.current = [];
|
||||
|
||||
if (isEvtWithFiles(event)) {
|
||||
Promise.resolve(getFilesFromEvent(event)).then(function (files) {
|
||||
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
||||
return;
|
||||
}
|
||||
|
||||
setFiles(files, event);
|
||||
}).catch(function (e) {
|
||||
return onErrCb(e);
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: "reset"
|
||||
});
|
||||
}, [getFilesFromEvent, setFiles, onErrCb, noDragEventsBubbling]); // Fn for opening the file dialog programmatically
|
||||
|
||||
var openFileDialog = useCallback(function () {
|
||||
// No point to use FS access APIs if context is not secure
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#feature_detection
|
||||
if (fsAccessApiWorksRef.current) {
|
||||
dispatch({
|
||||
type: "openDialog"
|
||||
});
|
||||
onFileDialogOpenCb(); // https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
|
||||
|
||||
var opts = {
|
||||
multiple: multiple,
|
||||
types: pickerTypes
|
||||
};
|
||||
window.showOpenFilePicker(opts).then(function (handles) {
|
||||
return getFilesFromEvent(handles);
|
||||
}).then(function (files) {
|
||||
setFiles(files, null);
|
||||
dispatch({
|
||||
type: "closeDialog"
|
||||
});
|
||||
}).catch(function (e) {
|
||||
// AbortError means the user canceled
|
||||
if (isAbort(e)) {
|
||||
onFileDialogCancelCb(e);
|
||||
dispatch({
|
||||
type: "closeDialog"
|
||||
});
|
||||
} else if (isSecurityError(e)) {
|
||||
fsAccessApiWorksRef.current = false; // CORS, so cannot use this API
|
||||
// Try using the input
|
||||
|
||||
if (inputRef.current) {
|
||||
inputRef.current.value = null;
|
||||
inputRef.current.click();
|
||||
} else {
|
||||
onErrCb(new Error("Cannot open the file picker because the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API is not supported and no <input> was provided."));
|
||||
}
|
||||
} else {
|
||||
onErrCb(e);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputRef.current) {
|
||||
dispatch({
|
||||
type: "openDialog"
|
||||
});
|
||||
onFileDialogOpenCb();
|
||||
inputRef.current.value = null;
|
||||
inputRef.current.click();
|
||||
}
|
||||
}, [dispatch, onFileDialogOpenCb, onFileDialogCancelCb, useFsAccessApi, setFiles, onErrCb, pickerTypes, multiple]); // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
|
||||
|
||||
var onKeyDownCb = useCallback(function (event) {
|
||||
// Ignore keyboard events bubbling up the DOM tree
|
||||
if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === " " || event.key === "Enter" || event.keyCode === 32 || event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
openFileDialog();
|
||||
}
|
||||
}, [rootRef, openFileDialog]); // Update focus state for the dropzone
|
||||
|
||||
var onFocusCb = useCallback(function () {
|
||||
dispatch({
|
||||
type: "focus"
|
||||
});
|
||||
}, []);
|
||||
var onBlurCb = useCallback(function () {
|
||||
dispatch({
|
||||
type: "blur"
|
||||
});
|
||||
}, []); // Cb to open the file dialog when click occurs on the dropzone
|
||||
|
||||
var onClickCb = useCallback(function () {
|
||||
if (noClick) {
|
||||
return;
|
||||
} // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout()
|
||||
// to ensure React can handle state changes
|
||||
// See: https://github.com/react-dropzone/react-dropzone/issues/450
|
||||
|
||||
|
||||
if (isIeOrEdge()) {
|
||||
setTimeout(openFileDialog, 0);
|
||||
} else {
|
||||
openFileDialog();
|
||||
}
|
||||
}, [noClick, openFileDialog]);
|
||||
|
||||
var composeHandler = function composeHandler(fn) {
|
||||
return disabled ? null : fn;
|
||||
};
|
||||
|
||||
var composeKeyboardHandler = function composeKeyboardHandler(fn) {
|
||||
return noKeyboard ? null : composeHandler(fn);
|
||||
};
|
||||
|
||||
var composeDragHandler = function composeDragHandler(fn) {
|
||||
return noDrag ? null : composeHandler(fn);
|
||||
};
|
||||
|
||||
var stopPropagation = function stopPropagation(event) {
|
||||
if (noDragEventsBubbling) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
var getRootProps = useMemo(function () {
|
||||
return function () {
|
||||
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
||||
_ref2$refKey = _ref2.refKey,
|
||||
refKey = _ref2$refKey === void 0 ? "ref" : _ref2$refKey,
|
||||
role = _ref2.role,
|
||||
onKeyDown = _ref2.onKeyDown,
|
||||
onFocus = _ref2.onFocus,
|
||||
onBlur = _ref2.onBlur,
|
||||
onClick = _ref2.onClick,
|
||||
onDragEnter = _ref2.onDragEnter,
|
||||
onDragOver = _ref2.onDragOver,
|
||||
onDragLeave = _ref2.onDragLeave,
|
||||
onDrop = _ref2.onDrop,
|
||||
rest = _objectWithoutProperties(_ref2, _excluded3);
|
||||
|
||||
return _objectSpread(_objectSpread(_defineProperty({
|
||||
onKeyDown: composeKeyboardHandler(composeEventHandlers(onKeyDown, onKeyDownCb)),
|
||||
onFocus: composeKeyboardHandler(composeEventHandlers(onFocus, onFocusCb)),
|
||||
onBlur: composeKeyboardHandler(composeEventHandlers(onBlur, onBlurCb)),
|
||||
onClick: composeHandler(composeEventHandlers(onClick, onClickCb)),
|
||||
onDragEnter: composeDragHandler(composeEventHandlers(onDragEnter, onDragEnterCb)),
|
||||
onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)),
|
||||
onDragLeave: composeDragHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)),
|
||||
onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)),
|
||||
role: typeof role === "string" && role !== "" ? role : "presentation"
|
||||
}, refKey, rootRef), !disabled && !noKeyboard ? {
|
||||
tabIndex: 0
|
||||
} : {}), rest);
|
||||
};
|
||||
}, [rootRef, onKeyDownCb, onFocusCb, onBlurCb, onClickCb, onDragEnterCb, onDragOverCb, onDragLeaveCb, onDropCb, noKeyboard, noDrag, disabled]);
|
||||
var onInputElementClick = useCallback(function (event) {
|
||||
event.stopPropagation();
|
||||
}, []);
|
||||
var getInputProps = useMemo(function () {
|
||||
return function () {
|
||||
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
||||
_ref3$refKey = _ref3.refKey,
|
||||
refKey = _ref3$refKey === void 0 ? "ref" : _ref3$refKey,
|
||||
onChange = _ref3.onChange,
|
||||
onClick = _ref3.onClick,
|
||||
rest = _objectWithoutProperties(_ref3, _excluded4);
|
||||
|
||||
var inputProps = _defineProperty({
|
||||
accept: acceptAttr,
|
||||
multiple: multiple,
|
||||
type: "file",
|
||||
style: {
|
||||
border: 0,
|
||||
clip: "rect(0, 0, 0, 0)",
|
||||
clipPath: "inset(50%)",
|
||||
height: "1px",
|
||||
margin: "0 -1px -1px 0",
|
||||
overflow: "hidden",
|
||||
padding: 0,
|
||||
position: "absolute",
|
||||
width: "1px",
|
||||
whiteSpace: "nowrap"
|
||||
},
|
||||
onChange: composeHandler(composeEventHandlers(onChange, onDropCb)),
|
||||
onClick: composeHandler(composeEventHandlers(onClick, onInputElementClick)),
|
||||
tabIndex: -1
|
||||
}, refKey, inputRef);
|
||||
|
||||
return _objectSpread(_objectSpread({}, inputProps), rest);
|
||||
};
|
||||
}, [inputRef, accept, multiple, onDropCb, disabled]);
|
||||
return _objectSpread(_objectSpread({}, state), {}, {
|
||||
isFocused: isFocused && !disabled,
|
||||
getRootProps: getRootProps,
|
||||
getInputProps: getInputProps,
|
||||
rootRef: rootRef,
|
||||
inputRef: inputRef,
|
||||
open: composeHandler(openFileDialog)
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {DropzoneState} state
|
||||
* @param {{type: string} & DropzoneState} action
|
||||
* @returns {DropzoneState}
|
||||
*/
|
||||
|
||||
function reducer(state, action) {
|
||||
/* istanbul ignore next */
|
||||
switch (action.type) {
|
||||
case "focus":
|
||||
return _objectSpread(_objectSpread({}, state), {}, {
|
||||
isFocused: true
|
||||
});
|
||||
|
||||
case "blur":
|
||||
return _objectSpread(_objectSpread({}, state), {}, {
|
||||
isFocused: false
|
||||
});
|
||||
|
||||
case "openDialog":
|
||||
return _objectSpread(_objectSpread({}, initialState), {}, {
|
||||
isFileDialogActive: true
|
||||
});
|
||||
|
||||
case "closeDialog":
|
||||
return _objectSpread(_objectSpread({}, state), {}, {
|
||||
isFileDialogActive: false
|
||||
});
|
||||
|
||||
case "setDraggedFiles":
|
||||
return _objectSpread(_objectSpread({}, state), {}, {
|
||||
isDragActive: action.isDragActive,
|
||||
isDragAccept: action.isDragAccept,
|
||||
isDragReject: action.isDragReject
|
||||
});
|
||||
|
||||
case "setFiles":
|
||||
return _objectSpread(_objectSpread({}, state), {}, {
|
||||
acceptedFiles: action.acceptedFiles,
|
||||
fileRejections: action.fileRejections,
|
||||
isDragReject: action.isDragReject
|
||||
});
|
||||
|
||||
case "reset":
|
||||
return _objectSpread({}, initialState);
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
export { ErrorCode } from "./utils";
|
||||
342
frontend/node_modules/react-dropzone/dist/es/utils/index.js
generated
vendored
Normal file
342
frontend/node_modules/react-dropzone/dist/es/utils/index.js
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
||||
|
||||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
||||
|
||||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
||||
|
||||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
||||
|
||||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
|
||||
|
||||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
||||
|
||||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
||||
|
||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
||||
|
||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
||||
|
||||
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
||||
|
||||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
||||
|
||||
import _accepts from "attr-accept";
|
||||
var accepts = typeof _accepts === "function" ? _accepts : _accepts.default; // Error codes
|
||||
|
||||
export var FILE_INVALID_TYPE = "file-invalid-type";
|
||||
export var FILE_TOO_LARGE = "file-too-large";
|
||||
export var FILE_TOO_SMALL = "file-too-small";
|
||||
export var TOO_MANY_FILES = "too-many-files";
|
||||
export var ErrorCode = {
|
||||
FileInvalidType: FILE_INVALID_TYPE,
|
||||
FileTooLarge: FILE_TOO_LARGE,
|
||||
FileTooSmall: FILE_TOO_SMALL,
|
||||
TooManyFiles: TOO_MANY_FILES
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param {string} accept
|
||||
*/
|
||||
|
||||
export var getInvalidTypeRejectionErr = function getInvalidTypeRejectionErr() {
|
||||
var accept = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
|
||||
var acceptArr = accept.split(",");
|
||||
var msg = acceptArr.length > 1 ? "one of ".concat(acceptArr.join(", ")) : acceptArr[0];
|
||||
return {
|
||||
code: FILE_INVALID_TYPE,
|
||||
message: "File type must be ".concat(msg)
|
||||
};
|
||||
};
|
||||
export var getTooLargeRejectionErr = function getTooLargeRejectionErr(maxSize) {
|
||||
return {
|
||||
code: FILE_TOO_LARGE,
|
||||
message: "File is larger than ".concat(maxSize, " ").concat(maxSize === 1 ? "byte" : "bytes")
|
||||
};
|
||||
};
|
||||
export var getTooSmallRejectionErr = function getTooSmallRejectionErr(minSize) {
|
||||
return {
|
||||
code: FILE_TOO_SMALL,
|
||||
message: "File is smaller than ".concat(minSize, " ").concat(minSize === 1 ? "byte" : "bytes")
|
||||
};
|
||||
};
|
||||
export var TOO_MANY_FILES_REJECTION = {
|
||||
code: TOO_MANY_FILES,
|
||||
message: "Too many files"
|
||||
};
|
||||
/**
|
||||
* Check if file is accepted.
|
||||
*
|
||||
* Firefox versions prior to 53 return a bogus MIME type for every file drag,
|
||||
* so dragovers with that MIME type will always be accepted.
|
||||
*
|
||||
* @param {File} file
|
||||
* @param {string} accept
|
||||
* @returns
|
||||
*/
|
||||
|
||||
export function fileAccepted(file, accept) {
|
||||
var isAcceptable = file.type === "application/x-moz-file" || accepts(file, accept);
|
||||
return [isAcceptable, isAcceptable ? null : getInvalidTypeRejectionErr(accept)];
|
||||
}
|
||||
export function fileMatchSize(file, minSize, maxSize) {
|
||||
if (isDefined(file.size)) {
|
||||
if (isDefined(minSize) && isDefined(maxSize)) {
|
||||
if (file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)];
|
||||
if (file.size < minSize) return [false, getTooSmallRejectionErr(minSize)];
|
||||
} else if (isDefined(minSize) && file.size < minSize) return [false, getTooSmallRejectionErr(minSize)];else if (isDefined(maxSize) && file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)];
|
||||
}
|
||||
|
||||
return [true, null];
|
||||
}
|
||||
|
||||
function isDefined(value) {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {File[]} options.files
|
||||
* @param {string} [options.accept]
|
||||
* @param {number} [options.minSize]
|
||||
* @param {number} [options.maxSize]
|
||||
* @param {boolean} [options.multiple]
|
||||
* @param {number} [options.maxFiles]
|
||||
* @param {(f: File) => FileError|FileError[]|null} [options.validator]
|
||||
* @returns
|
||||
*/
|
||||
|
||||
|
||||
export function allFilesAccepted(_ref) {
|
||||
var files = _ref.files,
|
||||
accept = _ref.accept,
|
||||
minSize = _ref.minSize,
|
||||
maxSize = _ref.maxSize,
|
||||
multiple = _ref.multiple,
|
||||
maxFiles = _ref.maxFiles,
|
||||
validator = _ref.validator;
|
||||
|
||||
if (!multiple && files.length > 1 || multiple && maxFiles >= 1 && files.length > maxFiles) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return files.every(function (file) {
|
||||
var _fileAccepted = fileAccepted(file, accept),
|
||||
_fileAccepted2 = _slicedToArray(_fileAccepted, 1),
|
||||
accepted = _fileAccepted2[0];
|
||||
|
||||
var _fileMatchSize = fileMatchSize(file, minSize, maxSize),
|
||||
_fileMatchSize2 = _slicedToArray(_fileMatchSize, 1),
|
||||
sizeMatch = _fileMatchSize2[0];
|
||||
|
||||
var customErrors = validator ? validator(file) : null;
|
||||
return accepted && sizeMatch && !customErrors;
|
||||
});
|
||||
} // React's synthetic events has event.isPropagationStopped,
|
||||
// but to remain compatibility with other libs (Preact) fall back
|
||||
// to check event.cancelBubble
|
||||
|
||||
export function isPropagationStopped(event) {
|
||||
if (typeof event.isPropagationStopped === "function") {
|
||||
return event.isPropagationStopped();
|
||||
} else if (typeof event.cancelBubble !== "undefined") {
|
||||
return event.cancelBubble;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
export function isEvtWithFiles(event) {
|
||||
if (!event.dataTransfer) {
|
||||
return !!event.target && !!event.target.files;
|
||||
} // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
|
||||
|
||||
|
||||
return Array.prototype.some.call(event.dataTransfer.types, function (type) {
|
||||
return type === "Files" || type === "application/x-moz-file";
|
||||
});
|
||||
}
|
||||
export function isKindFile(item) {
|
||||
return _typeof(item) === "object" && item !== null && item.kind === "file";
|
||||
} // allow the entire document to be a drag target
|
||||
|
||||
export function onDocumentDragOver(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function isIe(userAgent) {
|
||||
return userAgent.indexOf("MSIE") !== -1 || userAgent.indexOf("Trident/") !== -1;
|
||||
}
|
||||
|
||||
function isEdge(userAgent) {
|
||||
return userAgent.indexOf("Edge/") !== -1;
|
||||
}
|
||||
|
||||
export function isIeOrEdge() {
|
||||
var userAgent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.navigator.userAgent;
|
||||
return isIe(userAgent) || isEdge(userAgent);
|
||||
}
|
||||
/**
|
||||
* This is intended to be used to compose event handlers
|
||||
* They are executed in order until one of them calls `event.isPropagationStopped()`.
|
||||
* Note that the check is done on the first invoke too,
|
||||
* meaning that if propagation was stopped before invoking the fns,
|
||||
* no handlers will be executed.
|
||||
*
|
||||
* @param {Function} fns the event hanlder functions
|
||||
* @return {Function} the event handler to add to an element
|
||||
*/
|
||||
|
||||
export function composeEventHandlers() {
|
||||
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
fns[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
return function (event) {
|
||||
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
||||
args[_key2 - 1] = arguments[_key2];
|
||||
}
|
||||
|
||||
return fns.some(function (fn) {
|
||||
if (!isPropagationStopped(event) && fn) {
|
||||
fn.apply(void 0, [event].concat(args));
|
||||
}
|
||||
|
||||
return isPropagationStopped(event);
|
||||
});
|
||||
};
|
||||
}
|
||||
/**
|
||||
* canUseFileSystemAccessAPI checks if the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API)
|
||||
* is supported by the browser.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
export function canUseFileSystemAccessAPI() {
|
||||
return "showOpenFilePicker" in window;
|
||||
}
|
||||
/**
|
||||
* Convert the `{accept}` dropzone prop to the
|
||||
* `{types}` option for https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
|
||||
*
|
||||
* @param {AcceptProp} accept
|
||||
* @returns {{accept: string[]}[]}
|
||||
*/
|
||||
|
||||
export function pickerOptionsFromAccept(accept) {
|
||||
if (isDefined(accept)) {
|
||||
var acceptForPicker = Object.entries(accept).filter(function (_ref2) {
|
||||
var _ref3 = _slicedToArray(_ref2, 2),
|
||||
mimeType = _ref3[0],
|
||||
ext = _ref3[1];
|
||||
|
||||
var ok = true;
|
||||
|
||||
if (!isMIMEType(mimeType)) {
|
||||
console.warn("Skipped \"".concat(mimeType, "\" because it is not a valid MIME type. Check https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types for a list of valid MIME types."));
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (!Array.isArray(ext) || !ext.every(isExt)) {
|
||||
console.warn("Skipped \"".concat(mimeType, "\" because an invalid file extension was provided."));
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}).reduce(function (agg, _ref4) {
|
||||
var _ref5 = _slicedToArray(_ref4, 2),
|
||||
mimeType = _ref5[0],
|
||||
ext = _ref5[1];
|
||||
|
||||
return _objectSpread(_objectSpread({}, agg), {}, _defineProperty({}, mimeType, ext));
|
||||
}, {});
|
||||
return [{
|
||||
// description is required due to https://crbug.com/1264708
|
||||
description: "Files",
|
||||
accept: acceptForPicker
|
||||
}];
|
||||
}
|
||||
|
||||
return accept;
|
||||
}
|
||||
/**
|
||||
* Convert the `{accept}` dropzone prop to an array of MIME types/extensions.
|
||||
* @param {AcceptProp} accept
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
export function acceptPropAsAcceptAttr(accept) {
|
||||
if (isDefined(accept)) {
|
||||
return Object.entries(accept).reduce(function (a, _ref6) {
|
||||
var _ref7 = _slicedToArray(_ref6, 2),
|
||||
mimeType = _ref7[0],
|
||||
ext = _ref7[1];
|
||||
|
||||
return [].concat(_toConsumableArray(a), [mimeType], _toConsumableArray(ext));
|
||||
}, []) // Silently discard invalid entries as pickerOptionsFromAccept warns about these
|
||||
.filter(function (v) {
|
||||
return isMIMEType(v) || isExt(v);
|
||||
}).join(",");
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Check if v is an exception caused by aborting a request (e.g window.showOpenFilePicker()).
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/DOMException.
|
||||
* @param {any} v
|
||||
* @returns {boolean} True if v is an abort exception.
|
||||
*/
|
||||
|
||||
export function isAbort(v) {
|
||||
return v instanceof DOMException && (v.name === "AbortError" || v.code === v.ABORT_ERR);
|
||||
}
|
||||
/**
|
||||
* Check if v is a security error.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/DOMException.
|
||||
* @param {any} v
|
||||
* @returns {boolean} True if v is a security error.
|
||||
*/
|
||||
|
||||
export function isSecurityError(v) {
|
||||
return v instanceof DOMException && (v.name === "SecurityError" || v.code === v.SECURITY_ERR);
|
||||
}
|
||||
/**
|
||||
* Check if v is a MIME type string.
|
||||
*
|
||||
* See accepted format: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers.
|
||||
*
|
||||
* @param {string} v
|
||||
*/
|
||||
|
||||
export function isMIMEType(v) {
|
||||
return v === "audio/*" || v === "video/*" || v === "image/*" || v === "text/*" || v === "application/*" || /\w+\/[-+.\w]+/g.test(v);
|
||||
}
|
||||
/**
|
||||
* Check if v is a file extension.
|
||||
* @param {string} v
|
||||
*/
|
||||
|
||||
export function isExt(v) {
|
||||
return /^.*\.[\w]+$/.test(v);
|
||||
}
|
||||
/**
|
||||
* @typedef {Object.<string, string[]>} AcceptProp
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FileError
|
||||
* @property {string} message
|
||||
* @property {ErrorCode|string} code
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {"file-invalid-type"|"file-too-large"|"file-too-small"|"too-many-files"} ErrorCode
|
||||
*/
|
||||
2
frontend/node_modules/react-dropzone/dist/index.js
generated
vendored
Normal file
2
frontend/node_modules/react-dropzone/dist/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5
frontend/node_modules/react-dropzone/examples/.eslintrc
generated
vendored
Normal file
5
frontend/node_modules/react-dropzone/examples/.eslintrc
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"globals": {
|
||||
"useDropzone": true
|
||||
}
|
||||
}
|
||||
144
frontend/node_modules/react-dropzone/examples/accept/README.md
generated
vendored
Normal file
144
frontend/node_modules/react-dropzone/examples/accept/README.md
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
By providing `accept` prop you can make the dropzone accept specific file types and reject the others.
|
||||
|
||||
The value must be an object with a common [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) as keys and an array of file extensions as values (similar to [showOpenFilePicker](https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker)'s types `accept` option).
|
||||
```js static
|
||||
useDropzone({
|
||||
accept: {
|
||||
'image/png': ['.png'],
|
||||
'text/html': ['.html', '.htm'],
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
For more information see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input.
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function Accept(props) {
|
||||
const {
|
||||
acceptedFiles,
|
||||
fileRejections,
|
||||
getRootProps,
|
||||
getInputProps
|
||||
} = useDropzone({
|
||||
accept: {
|
||||
'image/jpeg': [],
|
||||
'image/png': []
|
||||
}
|
||||
});
|
||||
|
||||
const acceptedFileItems = acceptedFiles.map(file => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
const fileRejectionItems = fileRejections.map(({ file, errors }) => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
<ul>
|
||||
{errors.map(e => (
|
||||
<li key={e.code}>{e.message}</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
));
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({ className: 'dropzone' })}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
<em>(Only *.jpeg and *.png images will be accepted)</em>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Accepted files</h4>
|
||||
<ul>{acceptedFileItems}</ul>
|
||||
<h4>Rejected files</h4>
|
||||
<ul>{fileRejectionItems}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<Accept />
|
||||
```
|
||||
|
||||
### Browser limitations
|
||||
|
||||
Because of HTML5 File API differences across different browsers during the drag, Dropzone might not be able to detect whether the files are accepted or rejected in Safari nor IE.
|
||||
|
||||
Furthermore, at this moment it's not possible to read file names (and thus, file extensions) during the drag operation. For that reason, if you want to react on different file types _during_ the drag operation, _you have to use_ mime types and not extensions! For example, the following example won't work even in Chrome:
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function Accept(props) {
|
||||
const {
|
||||
getRootProps,
|
||||
getInputProps,
|
||||
isDragActive,
|
||||
isDragAccept,
|
||||
isDragReject
|
||||
} = useDropzone({
|
||||
accept: {
|
||||
'image/jpeg': ['.jpeg', '.png']
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
{isDragAccept && (<p>All files will be accepted</p>)}
|
||||
{isDragReject && (<p>Some files will be rejected</p>)}
|
||||
{!isDragActive && (<p>Drop some files here ...</p>)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
<Accept />
|
||||
```
|
||||
|
||||
but this one will:
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function Accept(props) {
|
||||
const {
|
||||
getRootProps,
|
||||
getInputProps,
|
||||
isDragActive,
|
||||
isDragAccept,
|
||||
isDragReject
|
||||
} = useDropzone({
|
||||
accept: {
|
||||
'image/*': ['.jpeg', '.png']
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div {...getRootProps({className: "dropzone"})}>
|
||||
<input {...getInputProps()} />
|
||||
{isDragAccept && (<p>All files will be accepted</p>)}
|
||||
{isDragReject && (<p>Some files will be rejected</p>)}
|
||||
{!isDragActive && (<p>Drop some files here ...</p>)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
<Accept />
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
Mime type determination is not reliable across platforms. CSV files, for example, are reported as text/plain under macOS but as application/vnd.ms-excel under Windows. In some cases there might not be a mime type set at all.
|
||||
|
||||
70
frontend/node_modules/react-dropzone/examples/basic/README.md
generated
vendored
Normal file
70
frontend/node_modules/react-dropzone/examples/basic/README.md
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
The `useDropzone` hook just binds the necessary handlers to create a drag 'n' drop zone.
|
||||
Use the `getRootProps()` fn to get the props required for drag 'n' drop and use them on any element.
|
||||
For click and keydown behavior, use the `getInputProps()` fn and use the returned props on an `<input>`.
|
||||
|
||||
Furthermore, the hook supports folder drag 'n' drop by default. See [file-selector](https://github.com/react-dropzone/file-selector) for more info about supported browsers.
|
||||
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function Basic(props) {
|
||||
const {acceptedFiles, getRootProps, getInputProps} = useDropzone();
|
||||
|
||||
const files = acceptedFiles.map(file => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<Basic />
|
||||
```
|
||||
|
||||
Dropzone with `disabled` property:
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function Basic(props) {
|
||||
const {acceptedFiles, getRootProps, getInputProps} = useDropzone({
|
||||
disabled: true
|
||||
});
|
||||
|
||||
const files = acceptedFiles.map(file => (
|
||||
<li key={file.name}>
|
||||
{file.name} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone disabled'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<Basic />
|
||||
```
|
||||
45
frontend/node_modules/react-dropzone/examples/class-component/README.md
generated
vendored
Normal file
45
frontend/node_modules/react-dropzone/examples/class-component/README.md
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
If you're still using class components, you can use the [`<Dropzone>`](https://react-dropzone.js.org/#components) component provided by the lib:
|
||||
|
||||
```jsx harmony
|
||||
import React, {Component} from 'react';
|
||||
import Dropzone from 'react-dropzone';
|
||||
|
||||
class Basic extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.onDrop = (files) => {
|
||||
this.setState({files})
|
||||
};
|
||||
this.state = {
|
||||
files: []
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const files = this.state.files.map(file => (
|
||||
<li key={file.name}>
|
||||
{file.name} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
return (
|
||||
<Dropzone onDrop={this.onDrop}>
|
||||
{({getRootProps, getInputProps}) => (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
)}
|
||||
</Dropzone>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
<Basic />
|
||||
```
|
||||
164
frontend/node_modules/react-dropzone/examples/events/README.md
generated
vendored
Normal file
164
frontend/node_modules/react-dropzone/examples/events/README.md
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
If you'd like to prevent drag events propagation from the child to parent, you can use the `{noDragEventsBubbling}` property on the child:
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function OuterDropzone(props) {
|
||||
const {getRootProps} = useDropzone({
|
||||
// Note how this callback is never invoked if drop occurs on the inner dropzone
|
||||
onDrop: files => console.log(files)
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<InnerDropzone />
|
||||
<p>Outer dropzone</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InnerDropzone(props) {
|
||||
const {getRootProps} = useDropzone({noDragEventsBubbling: true});
|
||||
return (
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<p>Inner dropzone</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
<OuterDropzone />
|
||||
```
|
||||
|
||||
Note that internally we use `event.stopPropagation()` to achieve the behavior illustrated above, but this comes with its own [drawbacks](https://javascript.info/bubbling-and-capturing#stopping-bubbling).
|
||||
|
||||
If you'd like to selectively turn off the default dropzone behavior for `onClick`, use the `{noClick}` property:
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function DropzoneWithoutClick(props) {
|
||||
const {getRootProps, getInputProps, acceptedFiles} = useDropzone({noClick: true});
|
||||
const files = acceptedFiles.map(file => <li key={file.path}>{file.path}</li>);
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Dropzone without click events</p>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<DropzoneWithoutClick />
|
||||
```
|
||||
|
||||
If you'd like to selectively turn off the default dropzone behavior for `onKeyDown`, `onFocus` and `onBlur`, use the `{noKeyboard}` property:
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function DropzoneWithoutKeyboard(props) {
|
||||
const {getRootProps, getInputProps, acceptedFiles} = useDropzone({noKeyboard: true});
|
||||
const files = acceptedFiles.map(file => <li key={file.path}>{file.path}</li>);
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Dropzone without keyboard events</p>
|
||||
<em>(SPACE/ENTER and focus events are disabled)</em>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<DropzoneWithoutKeyboard />
|
||||
```
|
||||
|
||||
Or you can prevent the default behavior for both click and keyboard events if you omit the input:
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function DropzoneWithoutClick(props) {
|
||||
const {getRootProps, acceptedFiles} = useDropzone();
|
||||
const files = acceptedFiles.map(file => <li key={file.path}>{file.path}</li>);
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<p>Dropzone without click events</p>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<DropzoneWithoutClick />
|
||||
```
|
||||
|
||||
**NOTE** If the browser supports the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) and you've set the `useFsAccessApi` to true, removing the `<input>` has no effect.
|
||||
|
||||
If you'd like to selectively turn off the default dropzone behavior for drag events, use the `{noDrag}` property:
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function DropzoneWithoutDrag(props) {
|
||||
const {getRootProps, getInputProps, acceptedFiles} = useDropzone({noDrag: true});
|
||||
const files = acceptedFiles.map(file => <li key={file.path}>{file.path}</li>);
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Dropzone with no drag events</p>
|
||||
<em>(Drag 'n' drop is disabled)</em>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<DropzoneWithoutDrag />
|
||||
```
|
||||
|
||||
Keep in mind that if you provide your own callback handlers as well and use `event.stopPropagation()`, it will prevent the default dropzone behavior:
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import Dropzone from 'react-dropzone';
|
||||
|
||||
// Note that there will be nothing logged when files are dropped
|
||||
<Dropzone onDrop={files => console.log(files)}>
|
||||
{({getRootProps, getInputProps}) => (
|
||||
<div className="container">
|
||||
<div
|
||||
{...getRootProps({
|
||||
className: 'dropzone',
|
||||
onDrop: event => event.stopPropagation()
|
||||
})}
|
||||
>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
```
|
||||
90
frontend/node_modules/react-dropzone/examples/file-dialog/README.md
generated
vendored
Normal file
90
frontend/node_modules/react-dropzone/examples/file-dialog/README.md
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
You can programmatically invoke the default OS file prompt; just use the `open` method returned by the hook.
|
||||
|
||||
**Note** that for security reasons most browsers require popups and dialogues to originate from a direct user interaction (i.e. click).
|
||||
|
||||
If you are calling `open()` asynchronously, there’s a good chance it’s going to be blocked by the browser. So if you are calling `open()` asynchronously, be sure there is no more than *1000ms* delay between user interaction and `open()` call.
|
||||
|
||||
Due to the lack of official docs on this (at least we haven’t found any. If you know one, feel free to open PR), there is no guarantee that **allowed delay duration** will not be changed in later browser versions. Since implementations may differ between different browsers, avoid calling open asynchronously if possible.
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function Dropzone(props) {
|
||||
const {getRootProps, getInputProps, open, acceptedFiles} = useDropzone({
|
||||
// Disable click and keydown behavior
|
||||
noClick: true,
|
||||
noKeyboard: true
|
||||
});
|
||||
|
||||
const files = acceptedFiles.map(file => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here</p>
|
||||
<button type="button" onClick={open}>
|
||||
Open File Dialog
|
||||
</button>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
<Dropzone />
|
||||
```
|
||||
|
||||
Or use the `ref` exposed by the `<Dropzone>` component:
|
||||
|
||||
```jsx harmony
|
||||
import React, {createRef} from 'react';
|
||||
import Dropzone from 'react-dropzone';
|
||||
|
||||
const dropzoneRef = createRef();
|
||||
const openDialog = () => {
|
||||
// Note that the ref is set async,
|
||||
// so it might be null at some point
|
||||
if (dropzoneRef.current) {
|
||||
dropzoneRef.current.open()
|
||||
}
|
||||
};
|
||||
|
||||
// Disable click and keydown behavior on the <Dropzone>
|
||||
<Dropzone ref={dropzoneRef} noClick noKeyboard>
|
||||
{({getRootProps, getInputProps, acceptedFiles}) => {
|
||||
return (
|
||||
<div className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={openDialog}
|
||||
>
|
||||
Open File Dialog
|
||||
</button>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>
|
||||
{acceptedFiles.map(file => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Dropzone>
|
||||
```
|
||||
69
frontend/node_modules/react-dropzone/examples/forms/README.md
generated
vendored
Normal file
69
frontend/node_modules/react-dropzone/examples/forms/README.md
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
React-dropzone does not submit the files in form submissions by default.
|
||||
|
||||
If you need this behavior, you can add a hidden file input, and set the files into it.
|
||||
|
||||
|
||||
```jsx harmony
|
||||
import React, {useRef} from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function Dropzone(props) {
|
||||
const {required, name} = props;
|
||||
|
||||
const hiddenInputRef = useRef(null);
|
||||
|
||||
const {getRootProps, getInputProps, open, acceptedFiles} = useDropzone({
|
||||
onDrop: (incomingFiles) => {
|
||||
if (hiddenInputRef.current) {
|
||||
// Note the specific way we need to munge the file into the hidden input
|
||||
// https://stackoverflow.com/a/68182158/1068446
|
||||
const dataTransfer = new DataTransfer();
|
||||
incomingFiles.forEach((v) => {
|
||||
dataTransfer.items.add(v);
|
||||
});
|
||||
hiddenInputRef.current.files = dataTransfer.files;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const files = acceptedFiles.map(file => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
{/*
|
||||
Add a hidden file input
|
||||
Best to use opacity 0, so that the required validation message will appear on form submission
|
||||
*/}
|
||||
<input type ="file" name={name} required={required} style ={{opacity: 0}} ref={hiddenInputRef}/>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here</p>
|
||||
<button type="button" onClick={open}>
|
||||
Open File Dialog
|
||||
</button>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
<form onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Now get the form data as you regularly would
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const file = formData.get("my-file");
|
||||
alert(file.name);
|
||||
}}>
|
||||
<Dropzone name ="my-file" required/>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
```
|
||||
58
frontend/node_modules/react-dropzone/examples/maxFiles/README.md
generated
vendored
Normal file
58
frontend/node_modules/react-dropzone/examples/maxFiles/README.md
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
By providing `maxFiles` prop you can limit how many files the dropzone accepts.
|
||||
|
||||
**Note** that this prop is enabled when the `multiple` prop is enabled.
|
||||
The default value for this prop is 0, which means there's no limitation to how many files are accepted.
|
||||
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function AcceptMaxFiles(props) {
|
||||
const {
|
||||
acceptedFiles,
|
||||
fileRejections,
|
||||
getRootProps,
|
||||
getInputProps
|
||||
} = useDropzone({
|
||||
maxFiles:2
|
||||
});
|
||||
|
||||
const acceptedFileItems = acceptedFiles.map(file => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
const fileRejectionItems = fileRejections.map(({ file, errors }) => {
|
||||
return (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
<ul>
|
||||
{errors.map(e => <li key={e.code}>{e.message}</li>)}
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({ className: 'dropzone' })}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
<em>(2 files are the maximum number of files you can drop here)</em>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Accepted files</h4>
|
||||
<ul>{acceptedFileItems}</ul>
|
||||
<h4>Rejected files</h4>
|
||||
<ul>{fileRejectionItems}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<AcceptMaxFiles />
|
||||
```
|
||||
32
frontend/node_modules/react-dropzone/examples/no-jsx/README.md
generated
vendored
Normal file
32
frontend/node_modules/react-dropzone/examples/no-jsx/README.md
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
If you'd like to use [react without JSX](https://reactjs.org/docs/react-without-jsx.html) you can:
|
||||
|
||||
```js harmony
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
const e = React.createElement
|
||||
|
||||
function Basic () {
|
||||
const [files, setFiles] = useState([]);
|
||||
const onDrop = useCallback(files => setFiles(files), [setFiles]);
|
||||
|
||||
const {getRootProps, getInputProps} = useDropzone({onDrop});
|
||||
|
||||
const fileList = files.map(
|
||||
file => React.createElement('li', {key: file.name}, `${file.name} - ${file.size} bytes`)
|
||||
);
|
||||
|
||||
return e('section', {className: 'container'}, [
|
||||
e('div', getRootProps({className: 'dropzone', key: 'dropzone'}), [
|
||||
e('input', getInputProps({key: 'input'})),
|
||||
e('p', {key: 'desc'}, "Drag 'n' drop some files here, or click to select files")
|
||||
]),
|
||||
e('aside', {key: 'filesContainer'}, [
|
||||
e('h4', {key: 'title'}, 'Files'),
|
||||
e('ul', {key: 'fileList'}, fileList)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
Basic()
|
||||
```
|
||||
146
frontend/node_modules/react-dropzone/examples/pintura/README.md
generated
vendored
Normal file
146
frontend/node_modules/react-dropzone/examples/pintura/README.md
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
If you'd like to integrate the dropzone with the [Pintura](https://pqina.nl/pintura/?ref=react-dropzone) image editor, you just need to pass either of the selected images to the `openDefaultEditor()` method exported by Pintura:
|
||||
|
||||
```jsx static
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
// React Dropzone
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
|
||||
// Pintura Image Editor
|
||||
import 'pintura/pintura.css';
|
||||
import { openDefaultEditor } from 'pintura';
|
||||
|
||||
// Based on the default React Dropzone image thumbnail example
|
||||
// The `thumbButton` style positions the edit button in the bottom right corner of the thumbnail
|
||||
const thumbsContainer = {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 16,
|
||||
padding: 20,
|
||||
};
|
||||
|
||||
const thumb = {
|
||||
position: 'relative',
|
||||
display: 'inline-flex',
|
||||
borderRadius: 2,
|
||||
border: '1px solid #eaeaea',
|
||||
marginBottom: 8,
|
||||
marginRight: 8,
|
||||
width: 100,
|
||||
height: 100,
|
||||
padding: 4,
|
||||
boxSizing: 'border-box',
|
||||
};
|
||||
|
||||
const thumbInner = {
|
||||
display: 'flex',
|
||||
minWidth: 0,
|
||||
overflow: 'hidden',
|
||||
};
|
||||
|
||||
const img = {
|
||||
display: 'block',
|
||||
width: 'auto',
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
const thumbButton = {
|
||||
position: 'absolute',
|
||||
right: 10,
|
||||
bottom: 10,
|
||||
};
|
||||
|
||||
// This function is called when the user taps the edit button.
|
||||
// It opens the editor and returns the modified file when done
|
||||
const editImage = (image, done) => {
|
||||
const imageFile = image.pintura ? image.pintura.file : image;
|
||||
const imageState = image.pintura ? image.pintura.data : {};
|
||||
|
||||
const editor = openDefaultEditor({
|
||||
src: imageFile,
|
||||
imageState,
|
||||
});
|
||||
|
||||
editor.on('close', () => {
|
||||
// the user cancelled editing the image
|
||||
});
|
||||
|
||||
editor.on('process', ({ dest, imageState }) => {
|
||||
Object.assign(dest, {
|
||||
pintura: { file: imageFile, data: imageState },
|
||||
});
|
||||
done(dest);
|
||||
});
|
||||
};
|
||||
|
||||
function App() {
|
||||
const [files, setFiles] = useState([]);
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
accept: {
|
||||
'image/*': [],
|
||||
},
|
||||
onDrop: (acceptedFiles) => {
|
||||
setFiles(
|
||||
acceptedFiles.map((file) =>
|
||||
Object.assign(file, {
|
||||
preview: URL.createObjectURL(file),
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const thumbs = files.map((file, index) => (
|
||||
<div style={thumb} key={file.name}>
|
||||
<div style={thumbInner}>
|
||||
<img src={file.preview} style={img} alt="" />
|
||||
</div>
|
||||
<button
|
||||
style={thumbButton}
|
||||
onClick={() =>
|
||||
editImage(file, (output) => {
|
||||
const updatedFiles = [...files];
|
||||
|
||||
// replace original image with new image
|
||||
updatedFiles[index] = output;
|
||||
|
||||
// revoke preview URL for old image
|
||||
if (file.preview) URL.revokeObjectURL(file.preview);
|
||||
|
||||
// set new preview URL
|
||||
Object.assign(output, {
|
||||
preview: URL.createObjectURL(output),
|
||||
});
|
||||
|
||||
// update view
|
||||
setFiles(updatedFiles);
|
||||
})
|
||||
}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
));
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
// Make sure to revoke the Object URL to avoid memory leaks
|
||||
files.forEach((file) => URL.revokeObjectURL(file.preview));
|
||||
},
|
||||
[files]
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({ className: 'dropzone' })}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
<aside style={thumbsContainer}>{thumbs}</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
```
|
||||
55
frontend/node_modules/react-dropzone/examples/plugins/README.md
generated
vendored
Normal file
55
frontend/node_modules/react-dropzone/examples/plugins/README.md
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
The hook accepts a `getFilesFromEvent` prop that enhances the handling of dropped file system objects and allows more flexible use of them e.g. passing a function that accepts drop event of a folder and resolves it to an array of files adds plug-in functionality of folders drag-and-drop.
|
||||
|
||||
Though, note that the provided `getFilesFromEvent` fn must return a `Promise` with a list of `File` objects (or `DataTransferItem` of `{kind: 'file'}` when data cannot be accessed).
|
||||
Otherwise, the results will be discarded and none of the drag events callbacks will be invoked.
|
||||
|
||||
In case you need to extend the `File` with some additional properties, you should use [Object.defineProperty()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) so that the result will still pass through our filter:
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
function Plugin(props) {
|
||||
const {acceptedFiles, getRootProps, getInputProps} = useDropzone({
|
||||
getFilesFromEvent: event => myCustomFileGetter(event)
|
||||
});
|
||||
|
||||
const files = acceptedFiles.map(f => (
|
||||
<li key={f.name}>
|
||||
{f.name} has <strong>myProps</strong>: {f.myProp === true ? 'YES' : ''}
|
||||
</li>
|
||||
));
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Files</h4>
|
||||
<ul>{files}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
async function myCustomFileGetter(event) {
|
||||
const files = [];
|
||||
const fileList = event.dataTransfer ? event.dataTransfer.files : event.target.files;
|
||||
|
||||
for (var i = 0; i < fileList.length; i++) {
|
||||
const file = fileList.item(i);
|
||||
|
||||
Object.defineProperty(file, 'myProp', {
|
||||
value: true
|
||||
});
|
||||
|
||||
files.push(file);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
<Plugin />
|
||||
```
|
||||
86
frontend/node_modules/react-dropzone/examples/previews/README.md
generated
vendored
Normal file
86
frontend/node_modules/react-dropzone/examples/previews/README.md
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
Starting with version 7.0.0, the `{preview}` property generation on the [File](https://developer.mozilla.org/en-US/docs/Web/API/File) objects and the `{disablePreview}` property on the `<Dropzone>` have been removed.
|
||||
|
||||
If you need the `{preview}`, it can be easily achieved in the `onDrop()` callback:
|
||||
|
||||
```jsx harmony
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
const thumbsContainer = {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 16
|
||||
};
|
||||
|
||||
const thumb = {
|
||||
display: 'inline-flex',
|
||||
borderRadius: 2,
|
||||
border: '1px solid #eaeaea',
|
||||
marginBottom: 8,
|
||||
marginRight: 8,
|
||||
width: 100,
|
||||
height: 100,
|
||||
padding: 4,
|
||||
boxSizing: 'border-box'
|
||||
};
|
||||
|
||||
const thumbInner = {
|
||||
display: 'flex',
|
||||
minWidth: 0,
|
||||
overflow: 'hidden'
|
||||
};
|
||||
|
||||
const img = {
|
||||
display: 'block',
|
||||
width: 'auto',
|
||||
height: '100%'
|
||||
};
|
||||
|
||||
|
||||
function Previews(props) {
|
||||
const [files, setFiles] = useState([]);
|
||||
const {getRootProps, getInputProps} = useDropzone({
|
||||
accept: {
|
||||
'image/*': []
|
||||
},
|
||||
onDrop: acceptedFiles => {
|
||||
setFiles(acceptedFiles.map(file => Object.assign(file, {
|
||||
preview: URL.createObjectURL(file)
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
const thumbs = files.map(file => (
|
||||
<div style={thumb} key={file.name}>
|
||||
<div style={thumbInner}>
|
||||
<img
|
||||
src={file.preview}
|
||||
style={img}
|
||||
// Revoke data uri after image is loaded
|
||||
onLoad={() => { URL.revokeObjectURL(file.preview) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
useEffect(() => {
|
||||
// Make sure to revoke the data uris to avoid memory leaks, will run on unmount
|
||||
return () => files.forEach(file => URL.revokeObjectURL(file.preview));
|
||||
}, [files]);
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({className: 'dropzone'})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
<aside style={thumbsContainer}>
|
||||
{thumbs}
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<Previews />
|
||||
```
|
||||
126
frontend/node_modules/react-dropzone/examples/styling/README.md
generated
vendored
Normal file
126
frontend/node_modules/react-dropzone/examples/styling/README.md
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
The hook fn doesn't set any styles on either of the prop fns (`getRootProps()`/`getInputProps()`).
|
||||
|
||||
### Using inline styles
|
||||
|
||||
```jsx harmony
|
||||
import React, {useMemo} from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
const baseStyle = {
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '20px',
|
||||
borderWidth: 2,
|
||||
borderRadius: 2,
|
||||
borderColor: '#eeeeee',
|
||||
borderStyle: 'dashed',
|
||||
backgroundColor: '#fafafa',
|
||||
color: '#bdbdbd',
|
||||
outline: 'none',
|
||||
transition: 'border .24s ease-in-out'
|
||||
};
|
||||
|
||||
const focusedStyle = {
|
||||
borderColor: '#2196f3'
|
||||
};
|
||||
|
||||
const acceptStyle = {
|
||||
borderColor: '#00e676'
|
||||
};
|
||||
|
||||
const rejectStyle = {
|
||||
borderColor: '#ff1744'
|
||||
};
|
||||
|
||||
function StyledDropzone(props) {
|
||||
const {
|
||||
getRootProps,
|
||||
getInputProps,
|
||||
isFocused,
|
||||
isDragAccept,
|
||||
isDragReject
|
||||
} = useDropzone({accept: {'image/*': []}});
|
||||
|
||||
const style = useMemo(() => ({
|
||||
...baseStyle,
|
||||
...(isFocused ? focusedStyle : {}),
|
||||
...(isDragAccept ? acceptStyle : {}),
|
||||
...(isDragReject ? rejectStyle : {})
|
||||
}), [
|
||||
isFocused,
|
||||
isDragAccept,
|
||||
isDragReject
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div {...getRootProps({style})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
<StyledDropzone />
|
||||
```
|
||||
|
||||
### Using styled-components
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const getColor = (props) => {
|
||||
if (props.isDragAccept) {
|
||||
return '#00e676';
|
||||
}
|
||||
if (props.isDragReject) {
|
||||
return '#ff1744';
|
||||
}
|
||||
if (props.isFocused) {
|
||||
return '#2196f3';
|
||||
}
|
||||
return '#eeeeee';
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-width: 2px;
|
||||
border-radius: 2px;
|
||||
border-color: ${props => getColor(props)};
|
||||
border-style: dashed;
|
||||
background-color: #fafafa;
|
||||
color: #bdbdbd;
|
||||
outline: none;
|
||||
transition: border .24s ease-in-out;
|
||||
`;
|
||||
|
||||
function StyledDropzone(props) {
|
||||
const {
|
||||
getRootProps,
|
||||
getInputProps,
|
||||
isFocused,
|
||||
isDragAccept,
|
||||
isDragReject
|
||||
} = useDropzone({accept: {'image/*': []}});
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<Container {...getRootProps({isFocused, isDragAccept, isDragReject})}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
<StyledDropzone />
|
||||
```
|
||||
37
frontend/node_modules/react-dropzone/examples/theme.css
generated
vendored
Normal file
37
frontend/node_modules/react-dropzone/examples/theme.css
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.container > p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.container > em {
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.dropzone {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-width: 2px;
|
||||
border-radius: 2px;
|
||||
border-color: #eeeeee;
|
||||
border-style: dashed;
|
||||
background-color: #fafafa;
|
||||
color: #bdbdbd;
|
||||
outline: none;
|
||||
transition: border .24s ease-in-out;
|
||||
}
|
||||
|
||||
.dropzone:focus {
|
||||
border-color: #2196f3;
|
||||
}
|
||||
|
||||
.dropzone.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
68
frontend/node_modules/react-dropzone/examples/validator/README.md
generated
vendored
Normal file
68
frontend/node_modules/react-dropzone/examples/validator/README.md
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
By providing `validator` prop you can specify custom validation for files.
|
||||
|
||||
The value must be a function that accepts File object and returns null if file should be accepted or error object/array of error objects if file should be rejected.
|
||||
|
||||
```jsx harmony
|
||||
import React from 'react';
|
||||
import {useDropzone} from 'react-dropzone';
|
||||
|
||||
const maxLength = 20;
|
||||
|
||||
function nameLengthValidator(file) {
|
||||
if (file.name.length > maxLength) {
|
||||
return {
|
||||
code: "name-too-large",
|
||||
message: `Name is larger than ${maxLength} characters`
|
||||
};
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function CustomValidation(props) {
|
||||
const {
|
||||
acceptedFiles,
|
||||
fileRejections,
|
||||
getRootProps,
|
||||
getInputProps
|
||||
} = useDropzone({
|
||||
validator: nameLengthValidator
|
||||
});
|
||||
|
||||
const acceptedFileItems = acceptedFiles.map(file => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
const fileRejectionItems = fileRejections.map(({ file, errors }) => (
|
||||
<li key={file.path}>
|
||||
{file.path} - {file.size} bytes
|
||||
<ul>
|
||||
{errors.map(e => (
|
||||
<li key={e.code}>{e.message}</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
));
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div {...getRootProps({ className: 'dropzone' })}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
<em>(Only files with name less than 20 characters will be accepted)</em>
|
||||
</div>
|
||||
<aside>
|
||||
<h4>Accepted files</h4>
|
||||
<ul>{acceptedFileItems}</ul>
|
||||
<h4>Rejected files</h4>
|
||||
<ul>{fileRejectionItems}</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<CustomValidation />
|
||||
```
|
||||
|
||||
204
frontend/node_modules/react-dropzone/package.json
generated
vendored
Normal file
204
frontend/node_modules/react-dropzone/package.json
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"name": "react-dropzone",
|
||||
"description": "Simple HTML5 drag-drop zone with React.js",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/es/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./typings/react-dropzone.d.ts",
|
||||
"default": "./dist/es/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./typings/react-dropzone.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
".": [
|
||||
"./typings/react-dropzone.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"cz": "git-cz",
|
||||
"clean": "rimraf ./dist",
|
||||
"build": "yarn clean && yarn build:umd && yarn build:es",
|
||||
"build:umd": "cross-env NODE_ENV=es rollup -c",
|
||||
"build:es": "cross-env BABEL_ENV=es babel ./src --out-dir ./dist/es --ignore '**/*.spec.js'",
|
||||
"start": "styleguidist server",
|
||||
"styleguide": "styleguidist build",
|
||||
"test": "cross-env NODE_ENV=test yarn lint && jest --coverage && yarn typescript",
|
||||
"test:watch": "cross-env NODE_ENV=test jest --watch",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"commitmsg": "commitlint -e",
|
||||
"prepublish": "yarn build && yarn size",
|
||||
"_postinstall": "husky install",
|
||||
"prepublishOnly": "pinst --disable",
|
||||
"postpublish": "pinst --enable",
|
||||
"logo": "cd logo && sketchtool export artboards logo.sketch",
|
||||
"imagemin": "imagemin --out-dir=logo --plugin=pngquant --plugin=svgo",
|
||||
"size": "size-limit",
|
||||
"size:why": "size-limit --why",
|
||||
"typescript": "tsc --project ./typings/tests"
|
||||
},
|
||||
"size-limit": [
|
||||
{
|
||||
"path": "dist/index.js",
|
||||
"limit": "17 KB"
|
||||
},
|
||||
{
|
||||
"path": "dist/es/index.js",
|
||||
"limit": "17 KB"
|
||||
}
|
||||
],
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"eslint . --fix"
|
||||
],
|
||||
"*.ts": [
|
||||
"eslint ."
|
||||
],
|
||||
"*.{svg,png}": [
|
||||
"imagemin"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "@commitlint/prompt"
|
||||
}
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"clearMocks": true,
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/testSetup.js"
|
||||
],
|
||||
"coveragePathIgnorePatterns": [
|
||||
"/dist/",
|
||||
"/node_modules/",
|
||||
"<rootDir>/testSetup.js"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules/",
|
||||
"/dist/"
|
||||
]
|
||||
},
|
||||
"keywords": [
|
||||
"react-component",
|
||||
"react",
|
||||
"drag",
|
||||
"drop",
|
||||
"upload"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/react-dropzone/react-dropzone.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/react-dropzone/react-dropzone/issues"
|
||||
},
|
||||
"homepage": "https://github.com/react-dropzone/react-dropzone",
|
||||
"author": "Param Aggarwal",
|
||||
"contributors": [
|
||||
"Andrey Okonetchnikov <andrey@okonet.ru> (http://okonet.ru)",
|
||||
"Mike Olson <me@mwolson.org>",
|
||||
"Param Aggarwal",
|
||||
"Tyler Waters <tyler.waters@gmail.com>",
|
||||
"Rick Markins <rmarkins@gmail.com>",
|
||||
"Roland Groza <rolandjitsu@gmail.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8 || 18.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"attr-accept": "^2.2.4",
|
||||
"file-selector": "^2.1.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.16.8",
|
||||
"@babel/core": "^7.16.12",
|
||||
"@babel/eslint-parser": "^7.16.5",
|
||||
"@babel/plugin-external-helpers": "^7.16.7",
|
||||
"@babel/plugin-proposal-do-expressions": "^7.16.7",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.16.7",
|
||||
"@babel/plugin-proposal-logical-assignment-operators": "^7.16.7",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
||||
"@babel/plugin-proposal-pipeline-operator": "^7.16.7",
|
||||
"@babel/plugin-transform-runtime": "^7.16.10",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"@babel/register": "^7.16.9",
|
||||
"@commitlint/cli": "^16.1.0",
|
||||
"@commitlint/config-angular": "^16.0.0",
|
||||
"@commitlint/prompt": "^16.1.0",
|
||||
"@commitlint/prompt-cli": "^16.1.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||
"@size-limit/preset-small-lib": "^7.0.5",
|
||||
"@size-limit/webpack": "^7.0.5",
|
||||
"@size-limit/webpack-why": "^7.0.5",
|
||||
"@testing-library/dom": "^8.11.3",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^13.1.1",
|
||||
"@testing-library/react-hooks": "^8.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||
"@typescript-eslint/parser": "^5.10.1",
|
||||
"babel-jest": "^27.4.6",
|
||||
"babel-plugin-add-module-exports": "^1.0.4",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"commitizen": "^4.2.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"husky": "^7.0.4",
|
||||
"imagemin-cli": "^8.0.0",
|
||||
"imagemin-pngquant": "^10.0.0",
|
||||
"jest": "^27.4.7",
|
||||
"lint-staged": "^12.3.2",
|
||||
"markdownlint-cli": "^0.30.0",
|
||||
"pinst": "^2.1.6",
|
||||
"prettier": "^2.5.1",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-styleguidist": "^11.2.0",
|
||||
"react-test-renderer": "^18.1.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.66.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"size-limit": "^7.0.5",
|
||||
"style-loader": "^3.3.1",
|
||||
"styled-components": "^5.3.3",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.67.0",
|
||||
"webpack-blocks": "^2.1.0"
|
||||
},
|
||||
"typings": "typings/react-dropzone.d.ts",
|
||||
"version": "14.3.5",
|
||||
"engines": {
|
||||
"node": ">= 10.13"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
],
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"resolutions": {
|
||||
"node-sass": "^9.0.0"
|
||||
}
|
||||
}
|
||||
33
frontend/node_modules/react-dropzone/rollup.config.js
generated
vendored
Normal file
33
frontend/node_modules/react-dropzone/rollup.config.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
const { nodeResolve } = require("@rollup/plugin-node-resolve");
|
||||
const commonjs = require("@rollup/plugin-commonjs");
|
||||
const { babel } = require("@rollup/plugin-babel");
|
||||
const { terser } = require("rollup-plugin-terser");
|
||||
|
||||
const umdGlobals = {
|
||||
react: "React",
|
||||
"prop-types": "PropTypes",
|
||||
};
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
input: "./src/index.js",
|
||||
output: {
|
||||
file: "dist/index.js",
|
||||
format: "umd",
|
||||
name: "reactDropzone",
|
||||
globals: umdGlobals,
|
||||
sourcemap: "inline",
|
||||
exports: "named",
|
||||
},
|
||||
external: Object.keys(umdGlobals),
|
||||
plugins: [
|
||||
nodeResolve(),
|
||||
commonjs({ include: "**/node_modules/**" }),
|
||||
babel({
|
||||
exclude: "**/node_modules/**",
|
||||
babelHelpers: "bundled",
|
||||
}),
|
||||
terser(),
|
||||
],
|
||||
},
|
||||
];
|
||||
37
frontend/node_modules/react-dropzone/src/.eslintrc
generated
vendored
Normal file
37
frontend/node_modules/react-dropzone/src/.eslintrc
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"plugins": [
|
||||
"import",
|
||||
"jsx-a11y",
|
||||
"prettier",
|
||||
"react",
|
||||
"react-hooks"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"react-hooks/rules-of-hooks": 2,
|
||||
"react/forbid-prop-types": 0,
|
||||
"react/require-default-props": 0
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
frontend/node_modules/react-dropzone/src/__snapshots__/index.spec.js.snap
generated
vendored
Normal file
3
frontend/node_modules/react-dropzone/src/__snapshots__/index.spec.js.snap
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`useDropzone() hook behavior renders the root and input nodes with the necessary props 1`] = `"<div role=\\"presentation\\" tabindex=\\"0\\"><input multiple=\\"\\" type=\\"file\\" style=\\"border: 0px; clip: rect(0px, 0px, 0px, 0px); clip-path: inset(50%); height: 1px; margin: 0px -1px -1px 0px; overflow: hidden; padding: 0px; position: absolute; width: 1px; white-space: nowrap;\\" tabindex=\\"-1\\"></div>"`;
|
||||
1042
frontend/node_modules/react-dropzone/src/index.js
generated
vendored
Executable file
1042
frontend/node_modules/react-dropzone/src/index.js
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
3640
frontend/node_modules/react-dropzone/src/index.spec.js
generated
vendored
Normal file
3640
frontend/node_modules/react-dropzone/src/index.spec.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
336
frontend/node_modules/react-dropzone/src/utils/index.js
generated
vendored
Normal file
336
frontend/node_modules/react-dropzone/src/utils/index.js
generated
vendored
Normal file
@@ -0,0 +1,336 @@
|
||||
import _accepts from "attr-accept";
|
||||
|
||||
const accepts = typeof _accepts === "function" ? _accepts : _accepts.default;
|
||||
|
||||
// Error codes
|
||||
export const FILE_INVALID_TYPE = "file-invalid-type";
|
||||
export const FILE_TOO_LARGE = "file-too-large";
|
||||
export const FILE_TOO_SMALL = "file-too-small";
|
||||
export const TOO_MANY_FILES = "too-many-files";
|
||||
|
||||
export const ErrorCode = {
|
||||
FileInvalidType: FILE_INVALID_TYPE,
|
||||
FileTooLarge: FILE_TOO_LARGE,
|
||||
FileTooSmall: FILE_TOO_SMALL,
|
||||
TooManyFiles: TOO_MANY_FILES,
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} accept
|
||||
*/
|
||||
export const getInvalidTypeRejectionErr = (accept = "") => {
|
||||
const acceptArr = accept.split(",");
|
||||
const msg =
|
||||
acceptArr.length > 1 ? `one of ${acceptArr.join(", ")}` : acceptArr[0];
|
||||
|
||||
return {
|
||||
code: FILE_INVALID_TYPE,
|
||||
message: `File type must be ${msg}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getTooLargeRejectionErr = (maxSize) => {
|
||||
return {
|
||||
code: FILE_TOO_LARGE,
|
||||
message: `File is larger than ${maxSize} ${
|
||||
maxSize === 1 ? "byte" : "bytes"
|
||||
}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getTooSmallRejectionErr = (minSize) => {
|
||||
return {
|
||||
code: FILE_TOO_SMALL,
|
||||
message: `File is smaller than ${minSize} ${
|
||||
minSize === 1 ? "byte" : "bytes"
|
||||
}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const TOO_MANY_FILES_REJECTION = {
|
||||
code: TOO_MANY_FILES,
|
||||
message: "Too many files",
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if file is accepted.
|
||||
*
|
||||
* Firefox versions prior to 53 return a bogus MIME type for every file drag,
|
||||
* so dragovers with that MIME type will always be accepted.
|
||||
*
|
||||
* @param {File} file
|
||||
* @param {string} accept
|
||||
* @returns
|
||||
*/
|
||||
export function fileAccepted(file, accept) {
|
||||
const isAcceptable =
|
||||
file.type === "application/x-moz-file" || accepts(file, accept);
|
||||
return [
|
||||
isAcceptable,
|
||||
isAcceptable ? null : getInvalidTypeRejectionErr(accept),
|
||||
];
|
||||
}
|
||||
|
||||
export function fileMatchSize(file, minSize, maxSize) {
|
||||
if (isDefined(file.size)) {
|
||||
if (isDefined(minSize) && isDefined(maxSize)) {
|
||||
if (file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)];
|
||||
if (file.size < minSize) return [false, getTooSmallRejectionErr(minSize)];
|
||||
} else if (isDefined(minSize) && file.size < minSize)
|
||||
return [false, getTooSmallRejectionErr(minSize)];
|
||||
else if (isDefined(maxSize) && file.size > maxSize)
|
||||
return [false, getTooLargeRejectionErr(maxSize)];
|
||||
}
|
||||
return [true, null];
|
||||
}
|
||||
|
||||
function isDefined(value) {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {File[]} options.files
|
||||
* @param {string} [options.accept]
|
||||
* @param {number} [options.minSize]
|
||||
* @param {number} [options.maxSize]
|
||||
* @param {boolean} [options.multiple]
|
||||
* @param {number} [options.maxFiles]
|
||||
* @param {(f: File) => FileError|FileError[]|null} [options.validator]
|
||||
* @returns
|
||||
*/
|
||||
export function allFilesAccepted({
|
||||
files,
|
||||
accept,
|
||||
minSize,
|
||||
maxSize,
|
||||
multiple,
|
||||
maxFiles,
|
||||
validator,
|
||||
}) {
|
||||
if (
|
||||
(!multiple && files.length > 1) ||
|
||||
(multiple && maxFiles >= 1 && files.length > maxFiles)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return files.every((file) => {
|
||||
const [accepted] = fileAccepted(file, accept);
|
||||
const [sizeMatch] = fileMatchSize(file, minSize, maxSize);
|
||||
const customErrors = validator ? validator(file) : null;
|
||||
return accepted && sizeMatch && !customErrors;
|
||||
});
|
||||
}
|
||||
|
||||
// React's synthetic events has event.isPropagationStopped,
|
||||
// but to remain compatibility with other libs (Preact) fall back
|
||||
// to check event.cancelBubble
|
||||
export function isPropagationStopped(event) {
|
||||
if (typeof event.isPropagationStopped === "function") {
|
||||
return event.isPropagationStopped();
|
||||
} else if (typeof event.cancelBubble !== "undefined") {
|
||||
return event.cancelBubble;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isEvtWithFiles(event) {
|
||||
if (!event.dataTransfer) {
|
||||
return !!event.target && !!event.target.files;
|
||||
}
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
|
||||
return Array.prototype.some.call(
|
||||
event.dataTransfer.types,
|
||||
(type) => type === "Files" || type === "application/x-moz-file"
|
||||
);
|
||||
}
|
||||
|
||||
export function isKindFile(item) {
|
||||
return typeof item === "object" && item !== null && item.kind === "file";
|
||||
}
|
||||
|
||||
// allow the entire document to be a drag target
|
||||
export function onDocumentDragOver(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function isIe(userAgent) {
|
||||
return (
|
||||
userAgent.indexOf("MSIE") !== -1 || userAgent.indexOf("Trident/") !== -1
|
||||
);
|
||||
}
|
||||
|
||||
function isEdge(userAgent) {
|
||||
return userAgent.indexOf("Edge/") !== -1;
|
||||
}
|
||||
|
||||
export function isIeOrEdge(userAgent = window.navigator.userAgent) {
|
||||
return isIe(userAgent) || isEdge(userAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is intended to be used to compose event handlers
|
||||
* They are executed in order until one of them calls `event.isPropagationStopped()`.
|
||||
* Note that the check is done on the first invoke too,
|
||||
* meaning that if propagation was stopped before invoking the fns,
|
||||
* no handlers will be executed.
|
||||
*
|
||||
* @param {Function} fns the event hanlder functions
|
||||
* @return {Function} the event handler to add to an element
|
||||
*/
|
||||
export function composeEventHandlers(...fns) {
|
||||
return (event, ...args) =>
|
||||
fns.some((fn) => {
|
||||
if (!isPropagationStopped(event) && fn) {
|
||||
fn(event, ...args);
|
||||
}
|
||||
return isPropagationStopped(event);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* canUseFileSystemAccessAPI checks if the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API)
|
||||
* is supported by the browser.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function canUseFileSystemAccessAPI() {
|
||||
return "showOpenFilePicker" in window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the `{accept}` dropzone prop to the
|
||||
* `{types}` option for https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
|
||||
*
|
||||
* @param {AcceptProp} accept
|
||||
* @returns {{accept: string[]}[]}
|
||||
*/
|
||||
export function pickerOptionsFromAccept(accept) {
|
||||
if (isDefined(accept)) {
|
||||
const acceptForPicker = Object.entries(accept)
|
||||
.filter(([mimeType, ext]) => {
|
||||
let ok = true;
|
||||
|
||||
if (!isMIMEType(mimeType)) {
|
||||
console.warn(
|
||||
`Skipped "${mimeType}" because it is not a valid MIME type. Check https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types for a list of valid MIME types.`
|
||||
);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (!Array.isArray(ext) || !ext.every(isExt)) {
|
||||
console.warn(
|
||||
`Skipped "${mimeType}" because an invalid file extension was provided.`
|
||||
);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
})
|
||||
.reduce(
|
||||
(agg, [mimeType, ext]) => ({
|
||||
...agg,
|
||||
[mimeType]: ext,
|
||||
}),
|
||||
{}
|
||||
);
|
||||
return [
|
||||
{
|
||||
// description is required due to https://crbug.com/1264708
|
||||
description: "Files",
|
||||
accept: acceptForPicker,
|
||||
},
|
||||
];
|
||||
}
|
||||
return accept;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the `{accept}` dropzone prop to an array of MIME types/extensions.
|
||||
* @param {AcceptProp} accept
|
||||
* @returns {string}
|
||||
*/
|
||||
export function acceptPropAsAcceptAttr(accept) {
|
||||
if (isDefined(accept)) {
|
||||
return (
|
||||
Object.entries(accept)
|
||||
.reduce((a, [mimeType, ext]) => [...a, mimeType, ...ext], [])
|
||||
// Silently discard invalid entries as pickerOptionsFromAccept warns about these
|
||||
.filter((v) => isMIMEType(v) || isExt(v))
|
||||
.join(",")
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if v is an exception caused by aborting a request (e.g window.showOpenFilePicker()).
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/DOMException.
|
||||
* @param {any} v
|
||||
* @returns {boolean} True if v is an abort exception.
|
||||
*/
|
||||
export function isAbort(v) {
|
||||
return (
|
||||
v instanceof DOMException &&
|
||||
(v.name === "AbortError" || v.code === v.ABORT_ERR)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if v is a security error.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/DOMException.
|
||||
* @param {any} v
|
||||
* @returns {boolean} True if v is a security error.
|
||||
*/
|
||||
export function isSecurityError(v) {
|
||||
return (
|
||||
v instanceof DOMException &&
|
||||
(v.name === "SecurityError" || v.code === v.SECURITY_ERR)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if v is a MIME type string.
|
||||
*
|
||||
* See accepted format: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers.
|
||||
*
|
||||
* @param {string} v
|
||||
*/
|
||||
export function isMIMEType(v) {
|
||||
return (
|
||||
v === "audio/*" ||
|
||||
v === "video/*" ||
|
||||
v === "image/*" ||
|
||||
v === "text/*" ||
|
||||
v === "application/*" ||
|
||||
/\w+\/[-+.\w]+/g.test(v)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if v is a file extension.
|
||||
* @param {string} v
|
||||
*/
|
||||
export function isExt(v) {
|
||||
return /^.*\.[\w]+$/.test(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object.<string, string[]>} AcceptProp
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FileError
|
||||
* @property {string} message
|
||||
* @property {ErrorCode|string} code
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {"file-invalid-type"|"file-too-large"|"file-too-small"|"too-many-files"} ErrorCode
|
||||
*/
|
||||
600
frontend/node_modules/react-dropzone/src/utils/index.spec.js
generated
vendored
Normal file
600
frontend/node_modules/react-dropzone/src/utils/index.spec.js
generated
vendored
Normal file
@@ -0,0 +1,600 @@
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
describe("fileMatchSize()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("should return true if the file object doesn't have a {size} property", () => {
|
||||
expect(utils.fileMatchSize({})).toEqual([true, null]);
|
||||
expect(utils.fileMatchSize({ size: null })).toEqual([true, null]);
|
||||
});
|
||||
|
||||
it("should return true if the minSize and maxSize were not provided", () => {
|
||||
expect(utils.fileMatchSize({ size: 100 })).toEqual([true, null]);
|
||||
expect(utils.fileMatchSize({ size: 100 }, undefined, undefined)).toEqual([
|
||||
true,
|
||||
null,
|
||||
]);
|
||||
expect(utils.fileMatchSize({ size: 100 }, null, null)).toEqual([
|
||||
true,
|
||||
null,
|
||||
]);
|
||||
});
|
||||
|
||||
it("should return true if the file {size} is within the [minSize, maxSize] range", () => {
|
||||
expect(utils.fileMatchSize({ size: 100 }, 10, 200)).toEqual([true, null]);
|
||||
expect(utils.fileMatchSize({ size: 100 }, 10, 99)).toEqual([
|
||||
false,
|
||||
{ code: "file-too-large", message: "File is larger than 99 bytes" },
|
||||
]);
|
||||
expect(utils.fileMatchSize({ size: 100 }, 101, 200)).toEqual([
|
||||
false,
|
||||
{ code: "file-too-small", message: "File is smaller than 101 bytes" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should return true if the file {size} is more than minSize", () => {
|
||||
expect(utils.fileMatchSize({ size: 100 }, 100)).toEqual([true, null]);
|
||||
expect(utils.fileMatchSize({ size: 100 }, 101)).toEqual([
|
||||
false,
|
||||
{ code: "file-too-small", message: "File is smaller than 101 bytes" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should return true if the file {size} is less than maxSize", () => {
|
||||
expect(utils.fileMatchSize({ size: 100 }, undefined, 100)).toEqual([
|
||||
true,
|
||||
null,
|
||||
]);
|
||||
expect(utils.fileMatchSize({ size: 100 }, null, 100)).toEqual([true, null]);
|
||||
expect(utils.fileMatchSize({ size: 100 }, undefined, 99)).toEqual([
|
||||
false,
|
||||
{ code: "file-too-large", message: "File is larger than 99 bytes" },
|
||||
]);
|
||||
expect(utils.fileMatchSize({ size: 100 }, null, 99)).toEqual([
|
||||
false,
|
||||
{ code: "file-too-large", message: "File is larger than 99 bytes" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isIeOrEdge", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("should return true for IE10", () => {
|
||||
const userAgent =
|
||||
"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729)";
|
||||
|
||||
expect(utils.isIeOrEdge(userAgent)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true for IE11", () => {
|
||||
const userAgent =
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; rv:11.0) like Gecko";
|
||||
expect(utils.isIeOrEdge(userAgent)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true for Edge", () => {
|
||||
const userAgent =
|
||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16258";
|
||||
|
||||
expect(utils.isIeOrEdge(userAgent)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for Chrome", () => {
|
||||
const userAgent =
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36";
|
||||
|
||||
expect(utils.isIeOrEdge(userAgent)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isKindFile()", () => {
|
||||
it('should return true for DataTransferItem of kind "file"', async () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
const utils = await import("./index");
|
||||
expect(utils.isKindFile({ kind: "file" })).toBe(true);
|
||||
expect(utils.isKindFile({ kind: "text/html" })).toBe(false);
|
||||
expect(utils.isKindFile({})).toBe(false);
|
||||
expect(utils.isKindFile(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isPropagationStopped()", () => {
|
||||
const trueFn = jest.fn(() => true);
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("should return result of isPropagationStopped() if isPropagationStopped exists", () => {
|
||||
expect(utils.isPropagationStopped({ isPropagationStopped: trueFn })).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("should return value of cancelBubble if isPropagationStopped doesnt exist and cancelBubble exists", () => {
|
||||
expect(utils.isPropagationStopped({ cancelBubble: true })).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false if isPropagationStopped and cancelBubble are missing", () => {
|
||||
expect(utils.isPropagationStopped({})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isEvtWithFiles()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("should return true if some dragged types are files", () => {
|
||||
expect(utils.isEvtWithFiles({ dataTransfer: { types: ["Files"] } })).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
utils.isEvtWithFiles({
|
||||
dataTransfer: { types: ["application/x-moz-file"] },
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
utils.isEvtWithFiles({
|
||||
dataTransfer: { types: ["Files", "application/x-moz-file"] },
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
utils.isEvtWithFiles({ dataTransfer: { types: ["text/plain"] } })
|
||||
).toBe(false);
|
||||
expect(
|
||||
utils.isEvtWithFiles({ dataTransfer: { types: ["text/html"] } })
|
||||
).toBe(false);
|
||||
expect(
|
||||
utils.isEvtWithFiles({
|
||||
dataTransfer: { types: ["Files", "application/test"] },
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
utils.isEvtWithFiles({
|
||||
dataTransfer: { types: ["application/x-moz-file", "application/test"] },
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true if the event has a target with files", () => {
|
||||
expect(utils.isEvtWithFiles({ target: { files: [] } })).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false otherwise", () => {
|
||||
expect(utils.isEvtWithFiles({})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("composeEventHandlers()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("returns a fn", () => {
|
||||
const fn = utils.composeEventHandlers(() => {});
|
||||
expect(typeof fn).toBe("function");
|
||||
});
|
||||
|
||||
it("runs every passed fn in order", () => {
|
||||
const fn1 = jest.fn();
|
||||
const fn2 = jest.fn();
|
||||
const fn = utils.composeEventHandlers(fn1, fn2);
|
||||
const event = { type: "click" };
|
||||
const data = { ping: true };
|
||||
fn(event, data);
|
||||
expect(fn1).toHaveBeenCalledWith(event, data);
|
||||
expect(fn2).toHaveBeenCalledWith(event, data);
|
||||
});
|
||||
|
||||
it("stops after first fn that calls stopPropagation()", () => {
|
||||
const fn1 = jest.fn().mockImplementation((event) => {
|
||||
Object.defineProperty(event, "cancelBubble", { value: true });
|
||||
return event;
|
||||
});
|
||||
const fn2 = jest.fn();
|
||||
const fn = utils.composeEventHandlers(fn1, fn2);
|
||||
const event = new MouseEvent("click");
|
||||
fn(event);
|
||||
expect(fn1).toHaveBeenCalledWith(event);
|
||||
expect(fn2).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("stops before first fn if bubble is already canceled", () => {
|
||||
const fn1 = jest.fn();
|
||||
const fn2 = jest.fn();
|
||||
const fn = utils.composeEventHandlers(fn1, fn2);
|
||||
const event = new MouseEvent("click");
|
||||
Object.defineProperty(event, "cancelBubble", { value: true });
|
||||
fn(event);
|
||||
expect(fn1).not.toHaveBeenCalled();
|
||||
expect(fn2).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("fileAccepted()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("accepts bogus firefox file", () => {
|
||||
const file = createFile("bogus.png", 100, "application/x-moz-file");
|
||||
expect(utils.fileAccepted(file, ".pdf")).toEqual([true, null]);
|
||||
});
|
||||
|
||||
it("accepts file when single accept criteria", () => {
|
||||
const file = createFile("hamster.pdf", 100, "application/pdf");
|
||||
expect(utils.fileAccepted(file, ".pdf")).toEqual([true, null]);
|
||||
});
|
||||
|
||||
it("accepts file when multiple accept criteria", () => {
|
||||
const file = createFile("hamster.pdf", 100, "application/pdf");
|
||||
expect(utils.fileAccepted(file, ".pdf,.png")).toEqual([true, null]);
|
||||
});
|
||||
|
||||
it("rejects file when single accept criteria", () => {
|
||||
const file = createFile("hamster.pdf", 100, "application/pdf");
|
||||
expect(utils.fileAccepted(file, ".png")).toEqual([
|
||||
false,
|
||||
{ code: "file-invalid-type", message: "File type must be .png" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("rejects file when multiple accept criteria", () => {
|
||||
const file = createFile("hamster.pdf", 100, "application/pdf");
|
||||
expect(utils.fileAccepted(file, ".gif,.png")).toEqual([
|
||||
false,
|
||||
{
|
||||
code: "file-invalid-type",
|
||||
message: "File type must be one of .gif, .png",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getTooLargeRejectionErr()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("prints byte when maxSize is 1", () => {
|
||||
expect(utils.getTooLargeRejectionErr(1).message).toEqual(
|
||||
"File is larger than 1 byte"
|
||||
);
|
||||
});
|
||||
|
||||
it("prints bytes when maxSize > 1", () => {
|
||||
expect(utils.getTooLargeRejectionErr(100).message).toEqual(
|
||||
"File is larger than 100 bytes"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getTooSmallRejectionErr()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("prints byte when minSize is 1", () => {
|
||||
expect(utils.getTooSmallRejectionErr(1).message).toEqual(
|
||||
"File is smaller than 1 byte"
|
||||
);
|
||||
});
|
||||
|
||||
it("prints bytes when minSize > 1", () => {
|
||||
expect(utils.getTooSmallRejectionErr(100).message).toEqual(
|
||||
"File is smaller than 100 bytes"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("allFilesAccepted()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
it("rejects file when multiple accept criteria", () => {
|
||||
const files = [
|
||||
createFile("hamster.pdf", 100, "application/pdf"),
|
||||
createFile("fish.pdf", 100, "application/pdf"),
|
||||
];
|
||||
const images = [
|
||||
createFile("cats.gif", 1234, "image/gif"),
|
||||
createFile("dogs.gif", 2345, "image/jpeg"),
|
||||
];
|
||||
expect(utils.allFilesAccepted({ files, multiple: true })).toEqual(true);
|
||||
expect(
|
||||
utils.allFilesAccepted({ files, multiple: true, maxFiles: 10 })
|
||||
).toEqual(true);
|
||||
expect(
|
||||
utils.allFilesAccepted({ files, multiple: false, maxFiles: 10 })
|
||||
).toEqual(false);
|
||||
expect(
|
||||
utils.allFilesAccepted({ files, multiple: true, accept: "image/jpeg" })
|
||||
).toEqual(false);
|
||||
expect(
|
||||
utils.allFilesAccepted({
|
||||
files: images,
|
||||
multiple: true,
|
||||
accept: "image/*",
|
||||
})
|
||||
).toEqual(true);
|
||||
expect(
|
||||
utils.allFilesAccepted({ files, multiple: true, minSize: 110 })
|
||||
).toEqual(false);
|
||||
expect(
|
||||
utils.allFilesAccepted({ files, multiple: true, maxSize: 99 })
|
||||
).toEqual(false);
|
||||
expect(
|
||||
utils.allFilesAccepted({ files, multiple: true, maxFiles: 1 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
utils.allFilesAccepted({
|
||||
files,
|
||||
validator: () => ({ code: "not-allowed", message: "Cannot do this!" }),
|
||||
})
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ErrorCode", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("should exist and have known error code properties", () => {
|
||||
expect(utils.ErrorCode.FileInvalidType).toEqual(utils.FILE_INVALID_TYPE);
|
||||
expect(utils.ErrorCode.FileTooLarge).toEqual(utils.FILE_TOO_LARGE);
|
||||
expect(utils.ErrorCode.FileTooSmall).toEqual(utils.FILE_TOO_SMALL);
|
||||
expect(utils.ErrorCode.TooManyFiles).toEqual(utils.TOO_MANY_FILES);
|
||||
});
|
||||
});
|
||||
|
||||
describe("canUseFileSystemAccessAPI()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("should return false if not", () => {
|
||||
expect(utils.canUseFileSystemAccessAPI()).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true if yes", () => {
|
||||
// TODO: If we use these in other tests, restore once test is done
|
||||
window.showOpenFilePicker = jest.fn();
|
||||
expect(utils.canUseFileSystemAccessAPI()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pickerOptionsFromAccept()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("converts the {accept} prop to file picker options", () => {
|
||||
expect(
|
||||
utils.pickerOptionsFromAccept({
|
||||
"image/*": [".png", ".jpg"], // ok
|
||||
"text/*": [".txt", ".pdf"], // ok
|
||||
"audio/*": ["mp3"], // not ok
|
||||
"*": [".p12"], // not ok
|
||||
})
|
||||
).toEqual([
|
||||
{
|
||||
description: "Files",
|
||||
accept: {
|
||||
"image/*": [".png", ".jpg"],
|
||||
"text/*": [".txt", ".pdf"],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("acceptPropAsAcceptAttr()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("converts {accept} to an array of strings", () => {
|
||||
expect(
|
||||
utils.acceptPropAsAcceptAttr({
|
||||
"image/*": [".png", ".jpg"],
|
||||
"text/*": [".txt", ".pdf"],
|
||||
"audio/*": ["mp3"], // `mp3` not ok
|
||||
"*": [".p12"], // `*` not ok
|
||||
})
|
||||
).toEqual("image/*,.png,.jpg,text/*,.txt,.pdf,audio/*,.p12");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isMIMEType()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("checks that the value is a valid MIME type string", () => {
|
||||
expect(utils.isMIMEType("text/html")).toBe(true);
|
||||
expect(utils.isMIMEType("text/*")).toBe(true);
|
||||
expect(utils.isMIMEType("image/*")).toBe(true);
|
||||
expect(utils.isMIMEType("video/*")).toBe(true);
|
||||
expect(utils.isMIMEType("audio/*")).toBe(true);
|
||||
expect(utils.isMIMEType("application/*")).toBe(true);
|
||||
expect(utils.isMIMEType("test/*")).toBe(false);
|
||||
expect(utils.isMIMEType("text")).toBe(false);
|
||||
expect(utils.isMIMEType("")).toBe(false);
|
||||
expect(utils.isMIMEType(undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isExt()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("checks that the value is a valid file extension", () => {
|
||||
expect(utils.isExt(".jpg")).toBe(true);
|
||||
expect(utils.isExt("me.jpg")).toBe(true);
|
||||
expect(utils.isExt("me.prev.png")).toBe(true);
|
||||
expect(utils.isExt("")).toBe(false);
|
||||
expect(utils.isExt("text")).toBe(false);
|
||||
expect(utils.isExt(undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isAbort()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("should work as expected", () => {
|
||||
expect(utils.isAbort(new DOMException())).toBe(false);
|
||||
expect(utils.isAbort(new DOMException("some err"))).toBe(false);
|
||||
expect(utils.isAbort(new DOMException("some err", "Noop"))).toBe(false);
|
||||
expect(utils.isAbort(new DOMException("some err", "AbortError"))).toBe(
|
||||
true
|
||||
);
|
||||
const err = new DOMException("some err");
|
||||
const e = new Proxy(err, {
|
||||
get(t, p) {
|
||||
if (p === "code") {
|
||||
return 20;
|
||||
}
|
||||
return t[p];
|
||||
},
|
||||
});
|
||||
expect(utils.isAbort(e)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isSecurityError()", () => {
|
||||
/**
|
||||
* @constant
|
||||
* @type {import('./index')}
|
||||
*/
|
||||
let utils;
|
||||
beforeEach(async () => {
|
||||
utils = await import("./index");
|
||||
});
|
||||
|
||||
it("should work as expected", () => {
|
||||
expect(utils.isSecurityError(new DOMException())).toBe(false);
|
||||
expect(utils.isSecurityError(new DOMException("some err"))).toBe(false);
|
||||
expect(utils.isSecurityError(new DOMException("some err", "Noop"))).toBe(
|
||||
false
|
||||
);
|
||||
expect(
|
||||
utils.isSecurityError(new DOMException("some err", "SecurityError"))
|
||||
).toBe(true);
|
||||
const err = new DOMException("some err");
|
||||
const e = new Proxy(err, {
|
||||
get(t, p) {
|
||||
if (p === "code") {
|
||||
return 18;
|
||||
}
|
||||
return t[p];
|
||||
},
|
||||
});
|
||||
expect(utils.isSecurityError(e)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
function createFile(name, size, type) {
|
||||
const file = new File([], name, { type });
|
||||
Object.defineProperty(file, "size", {
|
||||
get() {
|
||||
return size;
|
||||
},
|
||||
});
|
||||
return file;
|
||||
}
|
||||
103
frontend/node_modules/react-dropzone/styleguide.config.js
generated
vendored
Normal file
103
frontend/node_modules/react-dropzone/styleguide.config.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/* eslint import/no-extraneous-dependencies: 0 */
|
||||
const path = require("path");
|
||||
const { createConfig, babel, css, devServer } = require("webpack-blocks");
|
||||
|
||||
// https://react-styleguidist.js.org/docs/configuration.html
|
||||
module.exports = {
|
||||
title: "react-dropzone",
|
||||
styleguideDir: path.join(__dirname, "styleguide"),
|
||||
template: {
|
||||
favicon:
|
||||
"https://github.com/react-dropzone/react-dropzone/raw/master/logo/logo.png",
|
||||
},
|
||||
webpackConfig: createConfig([
|
||||
babel(),
|
||||
css(),
|
||||
devServer({
|
||||
disableHostCheck: true,
|
||||
host: "0.0.0.0",
|
||||
}),
|
||||
]),
|
||||
exampleMode: "expand",
|
||||
usageMode: "expand",
|
||||
showSidebar: true,
|
||||
serverPort: 8080,
|
||||
moduleAliases: {
|
||||
"react-dropzone": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
require: [path.join(__dirname, "examples/theme.css")],
|
||||
sections: [
|
||||
{
|
||||
name: "",
|
||||
content: "README.md",
|
||||
},
|
||||
// TODO: Figure out how to document the hook
|
||||
// See https://github.com/reactjs/react-docgen/issues/332
|
||||
{
|
||||
name: "Components",
|
||||
components: "./src/index.js",
|
||||
},
|
||||
{
|
||||
name: "Examples",
|
||||
sections: [
|
||||
{
|
||||
name: "Basic example",
|
||||
content: "examples/basic/README.md",
|
||||
},
|
||||
{
|
||||
name: "Event Propagation",
|
||||
content: "examples/events/README.md",
|
||||
},
|
||||
{
|
||||
name: "Forms",
|
||||
content: "examples/forms/README.md",
|
||||
},
|
||||
{
|
||||
name: "Styling Dropzone",
|
||||
content: "examples/styling/README.md",
|
||||
},
|
||||
{
|
||||
name: "Accepting specific file types",
|
||||
content: "examples/accept/README.md",
|
||||
},
|
||||
{
|
||||
name: "Accepting specific number of files",
|
||||
content: "examples/maxFiles/README.md",
|
||||
},
|
||||
{
|
||||
name: "Custom validation",
|
||||
content: "examples/validator/README.md",
|
||||
},
|
||||
{
|
||||
name: "Opening File Dialog Programmatically",
|
||||
content: "examples/file-dialog/README.md",
|
||||
},
|
||||
{
|
||||
name: "Previews",
|
||||
content: "examples/previews/README.md",
|
||||
},
|
||||
{
|
||||
name: "Class Components",
|
||||
content: "examples/class-component/README.md",
|
||||
},
|
||||
{
|
||||
name: "No JSX",
|
||||
content: "examples/no-jsx/README.md",
|
||||
},
|
||||
{
|
||||
name: "Extending Dropzone",
|
||||
content: "examples/plugins/README.md",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Integrations",
|
||||
sections: [
|
||||
{
|
||||
name: "Pintura",
|
||||
content: "examples/pintura/README.md",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
8
frontend/node_modules/react-dropzone/testSetup.js
generated
vendored
Normal file
8
frontend/node_modules/react-dropzone/testSetup.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// https://www.npmjs.com/package/jest-dom
|
||||
require("@testing-library/jest-dom/extend-expect");
|
||||
|
||||
Object.defineProperty(global, "isSecureContext", {
|
||||
value: true,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
28
frontend/node_modules/react-dropzone/typings/.eslintrc
generated
vendored
Normal file
28
frontend/node_modules/react-dropzone/typings/.eslintrc
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true
|
||||
},
|
||||
"plugins": [
|
||||
"import",
|
||||
"prettier",
|
||||
"react",
|
||||
"react-hooks",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
}
|
||||
}
|
||||
100
frontend/node_modules/react-dropzone/typings/react-dropzone.d.ts
generated
vendored
Normal file
100
frontend/node_modules/react-dropzone/typings/react-dropzone.d.ts
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { FileWithPath } from "file-selector";
|
||||
export { FileWithPath };
|
||||
export default function Dropzone(
|
||||
props: DropzoneProps & React.RefAttributes<DropzoneRef>
|
||||
): JSX.Element;
|
||||
export function useDropzone(options?: DropzoneOptions): DropzoneState;
|
||||
|
||||
export interface DropzoneProps extends DropzoneOptions {
|
||||
children?(state: DropzoneState): JSX.Element;
|
||||
}
|
||||
|
||||
export enum ErrorCode {
|
||||
FileInvalidType = "file-invalid-type",
|
||||
FileTooLarge = "file-too-large",
|
||||
FileTooSmall = "file-too-small",
|
||||
TooManyFiles = "too-many-files",
|
||||
}
|
||||
|
||||
export interface FileError {
|
||||
message: string;
|
||||
code: ErrorCode | string;
|
||||
}
|
||||
|
||||
export interface FileRejection {
|
||||
file: FileWithPath;
|
||||
errors: readonly FileError[];
|
||||
}
|
||||
|
||||
export type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, PropTypes> & {
|
||||
accept?: Accept;
|
||||
minSize?: number;
|
||||
maxSize?: number;
|
||||
maxFiles?: number;
|
||||
preventDropOnDocument?: boolean;
|
||||
noClick?: boolean;
|
||||
noKeyboard?: boolean;
|
||||
noDrag?: boolean;
|
||||
noDragEventsBubbling?: boolean;
|
||||
disabled?: boolean;
|
||||
onDrop?: <T extends File>(
|
||||
acceptedFiles: T[],
|
||||
fileRejections: FileRejection[],
|
||||
event: DropEvent
|
||||
) => void;
|
||||
onDropAccepted?: <T extends File>(files: T[], event: DropEvent) => void;
|
||||
onDropRejected?: (fileRejections: FileRejection[], event: DropEvent) => void;
|
||||
getFilesFromEvent?: (
|
||||
event: DropEvent
|
||||
) => Promise<Array<File | DataTransferItem>>;
|
||||
onFileDialogCancel?: () => void;
|
||||
onFileDialogOpen?: () => void;
|
||||
onError?: (err: Error) => void;
|
||||
validator?: <T extends File>(
|
||||
file: T
|
||||
) => FileError | readonly FileError[] | null;
|
||||
useFsAccessApi?: boolean;
|
||||
autoFocus?: boolean;
|
||||
};
|
||||
|
||||
export type DropEvent =
|
||||
| React.DragEvent<HTMLElement>
|
||||
| React.ChangeEvent<HTMLInputElement>
|
||||
| DragEvent
|
||||
| Event;
|
||||
|
||||
export type DropzoneState = DropzoneRef & {
|
||||
isFocused: boolean;
|
||||
isDragActive: boolean;
|
||||
isDragAccept: boolean;
|
||||
isDragReject: boolean;
|
||||
isFileDialogActive: boolean;
|
||||
acceptedFiles: readonly FileWithPath[];
|
||||
fileRejections: readonly FileRejection[];
|
||||
rootRef: React.RefObject<HTMLElement>;
|
||||
inputRef: React.RefObject<HTMLInputElement>;
|
||||
getRootProps: <T extends DropzoneRootProps>(props?: T) => T;
|
||||
getInputProps: <T extends DropzoneInputProps>(props?: T) => T;
|
||||
};
|
||||
|
||||
export interface DropzoneRef {
|
||||
open: () => void;
|
||||
}
|
||||
|
||||
export interface DropzoneRootProps extends React.HTMLAttributes<HTMLElement> {
|
||||
refKey?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface DropzoneInputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
refKey?: string;
|
||||
}
|
||||
|
||||
type PropTypes = "multiple" | "onDragEnter" | "onDragOver" | "onDragLeave";
|
||||
|
||||
export interface Accept {
|
||||
[key: string]: readonly string[];
|
||||
}
|
||||
54
frontend/node_modules/react-dropzone/typings/tests/accept.tsx
generated
vendored
Normal file
54
frontend/node_modules/react-dropzone/typings/tests/accept.tsx
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import React from "react";
|
||||
import Dropzone from "../../";
|
||||
|
||||
export default class Accept extends React.Component {
|
||||
state = {
|
||||
accepted: [],
|
||||
rejected: [],
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<section>
|
||||
<div className="dropzone">
|
||||
<Dropzone
|
||||
accept={{
|
||||
"image/*": [".jpeg", ".png"],
|
||||
}}
|
||||
onDrop={(accepted, rejected) => {
|
||||
this.setState({ accepted, rejected });
|
||||
}}
|
||||
>
|
||||
{({ getRootProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<p>
|
||||
Try dropping some files here, or click to select files to
|
||||
upload.
|
||||
</p>
|
||||
<p>Only *.jpeg and *.png images will be accepted</p>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
</div>
|
||||
<aside>
|
||||
<h2>Accepted files</h2>
|
||||
<ul>
|
||||
{this.state.accepted.map((f) => (
|
||||
<li key={f.name}>
|
||||
{f.name} - {f.size} bytes
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<h2>Rejected files</h2>
|
||||
<ul>
|
||||
{this.state.rejected.map((f) => (
|
||||
<li key={f.name}>
|
||||
{f.name} - {f.size} bytes
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
46
frontend/node_modules/react-dropzone/typings/tests/all.tsx
generated
vendored
Normal file
46
frontend/node_modules/react-dropzone/typings/tests/all.tsx
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
import Dropzone from "../../";
|
||||
|
||||
export default class Test extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Dropzone
|
||||
onDrop={(acceptedFiles, fileRejections, event) =>
|
||||
console.log(acceptedFiles, fileRejections, event)
|
||||
}
|
||||
onDragEnter={(event) => console.log(event)}
|
||||
onDragOver={(event) => console.log(event)}
|
||||
onDragLeave={(event) => console.log(event)}
|
||||
onDropAccepted={(files, event) => console.log(files, event)}
|
||||
onDropRejected={(files, event) => console.log(files, event)}
|
||||
onFileDialogCancel={() => console.log("onFileDialogCancel invoked")}
|
||||
onFileDialogOpen={() => console.log("onFileDialogOpen invoked")}
|
||||
onError={(e) => console.log(e)}
|
||||
validator={(f) => ({ message: f.name, code: "" })}
|
||||
minSize={2000}
|
||||
maxSize={Infinity}
|
||||
maxFiles={100}
|
||||
preventDropOnDocument
|
||||
noClick={false}
|
||||
noKeyboard={false}
|
||||
noDrag={false}
|
||||
noDragEventsBubbling={false}
|
||||
disabled
|
||||
multiple={false}
|
||||
accept={{
|
||||
"image/*": [".png"],
|
||||
}}
|
||||
useFsAccessApi={false}
|
||||
autoFocus
|
||||
>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
53
frontend/node_modules/react-dropzone/typings/tests/basic.tsx
generated
vendored
Normal file
53
frontend/node_modules/react-dropzone/typings/tests/basic.tsx
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import Dropzone from "../../";
|
||||
import { FileWithPath } from "file-selector";
|
||||
|
||||
export default class Basic extends React.Component {
|
||||
state = { files: [] };
|
||||
|
||||
onDrop = (files: FileWithPath[]) => {
|
||||
this.setState({
|
||||
files,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<section>
|
||||
<div className="dropzone">
|
||||
<Dropzone onDrop={this.onDrop}>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>
|
||||
Try dropping some files here, or click to select files to
|
||||
upload.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
</div>
|
||||
<aside>
|
||||
<h2>Dropped files</h2>
|
||||
<ul>
|
||||
{this.state.files.map((f) => (
|
||||
<li key={f.name}>
|
||||
{f.name} - {f.size} bytes
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</aside>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const optional = (
|
||||
<Dropzone>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
);
|
||||
31
frontend/node_modules/react-dropzone/typings/tests/events.tsx
generated
vendored
Normal file
31
frontend/node_modules/react-dropzone/typings/tests/events.tsx
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
import Dropzone from "../../";
|
||||
|
||||
export class Events extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<section>
|
||||
<div className="dropzone">
|
||||
<Dropzone
|
||||
onDrop={(acceptedFiles, fileRejections, event) =>
|
||||
console.log(acceptedFiles, fileRejections, event)
|
||||
}
|
||||
onDragEnter={(event) => console.log(event)}
|
||||
onDragOver={(event) => console.log(event)}
|
||||
onDragLeave={(event) => console.log(event)}
|
||||
>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>
|
||||
Try dropping some files here, or click to select files to
|
||||
upload.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
20
frontend/node_modules/react-dropzone/typings/tests/file-dialog.tsx
generated
vendored
Normal file
20
frontend/node_modules/react-dropzone/typings/tests/file-dialog.tsx
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import Dropzone from "../../";
|
||||
|
||||
export const dropzone = (
|
||||
<Dropzone
|
||||
onDrop={(files) => console.log(files)}
|
||||
onFileDialogCancel={() => console.log("onFileDialogCancel invoked")}
|
||||
onFileDialogOpen={() => console.log("onFileDialogOpen invoked")}
|
||||
>
|
||||
{({ getRootProps, getInputProps, open }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drop some files here.</p>
|
||||
<button type="button" onClick={open}>
|
||||
Open file dialog
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
);
|
||||
15
frontend/node_modules/react-dropzone/typings/tests/hook.tsx
generated
vendored
Normal file
15
frontend/node_modules/react-dropzone/typings/tests/hook.tsx
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import { useDropzone, DropzoneProps } from "../../";
|
||||
|
||||
export const Dropzone = ({ children, ...opts }: DropzoneProps) => {
|
||||
const { ...state } = useDropzone(opts);
|
||||
return children(state);
|
||||
};
|
||||
|
||||
<Dropzone>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>;
|
||||
87
frontend/node_modules/react-dropzone/typings/tests/plugin.tsx
generated
vendored
Normal file
87
frontend/node_modules/react-dropzone/typings/tests/plugin.tsx
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import React, { Component } from "react";
|
||||
import Dropzone from "../../";
|
||||
|
||||
export class TestReactDragEvt extends Component {
|
||||
getFiles = async (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const files = Array.from(event.dataTransfer.files);
|
||||
return files;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Dropzone getFilesFromEvent={this.getFiles}>
|
||||
{({ getRootProps }) => <div {...getRootProps()} />}
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestDataTransferItems extends Component {
|
||||
getFiles = async (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const items = Array.from(event.dataTransfer.items);
|
||||
return items;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Dropzone getFilesFromEvent={this.getFiles}>
|
||||
{({ getRootProps }) => <div {...getRootProps()} />}
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestNativeDragEventEvt extends Component {
|
||||
getFiles = async (event: DragEvent) => {
|
||||
const files = Array.from(event.dataTransfer.files);
|
||||
return files;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Dropzone getFilesFromEvent={this.getFiles}>
|
||||
{({ getRootProps }) => <div {...getRootProps()} />}
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestChangeEvt extends Component {
|
||||
getFiles = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = Array.from(event.target.files);
|
||||
return files;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Dropzone getFilesFromEvent={this.getFiles}>
|
||||
{({ getRootProps }) => <div {...getRootProps()} />}
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestNativeEvt extends Component {
|
||||
getFiles = async (event: Event) => {
|
||||
const files = Array.from((event.target as HTMLInputElement).files);
|
||||
return files;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Dropzone getFilesFromEvent={this.getFiles}>
|
||||
{({ getRootProps }) => <div {...getRootProps()} />}
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
18
frontend/node_modules/react-dropzone/typings/tests/refs.tsx
generated
vendored
Normal file
18
frontend/node_modules/react-dropzone/typings/tests/refs.tsx
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import React, { createRef } from "react";
|
||||
import Dropzone, { DropzoneRef } from "../../";
|
||||
|
||||
const ref = createRef<DropzoneRef>();
|
||||
|
||||
export const dropzone = (
|
||||
<Dropzone ref={ref}>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<p>Drop some files here.</p>
|
||||
<button type="button" onClick={ref.current.open}>
|
||||
Open file dialog
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
);
|
||||
22
frontend/node_modules/react-dropzone/typings/tests/tsconfig.json
generated
vendored
Normal file
22
frontend/node_modules/react-dropzone/typings/tests/tsconfig.json
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
"module": "es2015",
|
||||
"target": "esnext",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./*.tsx"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user