@availity/mui-file-selector 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +61 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +65 -0
- package/dist/index.mjs +38 -0
- package/introduction.stories.mdx +7 -0
- package/jest.config.js +7 -0
- package/package.json +65 -0
- package/project.json +41 -0
- package/src/index.ts +1 -0
- package/src/lib/Dropzone.test.tsx +28 -0
- package/src/lib/Dropzone.tsx +185 -0
- package/src/lib/FileList.test.tsx +16 -0
- package/src/lib/FileList.tsx +70 -0
- package/src/lib/FilePickerBtn.test.tsx +24 -0
- package/src/lib/FilePickerBtn.tsx +74 -0
- package/src/lib/FilePickerInput.md +82 -0
- package/src/lib/FileRow.md +70 -0
- package/src/lib/FileSelector.stories.tsx +49 -0
- package/src/lib/FileSelector.test.tsx +17 -0
- package/src/lib/FileSelector.tsx +148 -0
- package/src/lib/FileTypesMessage.test.tsx +11 -0
- package/src/lib/FileTypesMessage.tsx +23 -0
- package/src/lib/HeaderMessage.test.tsx +11 -0
- package/src/lib/HeaderMessage.tsx +16 -0
- package/src/lib/UploadProgressBar.test.tsx +23 -0
- package/src/lib/UploadProgressBar.tsx +93 -0
- package/src/lib/useFileDelivery.tsx +75 -0
- package/src/lib/useUploadCore.tsx +23 -0
- package/src/lib/util.ts +42 -0
- package/tsconfig.json +5 -0
- package/tsconfig.spec.json +10 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
|
+
|
|
5
|
+
## 0.1.0 (2024-10-31)
|
|
6
|
+
|
|
7
|
+
### Dependency Updates
|
|
8
|
+
|
|
9
|
+
* `mui-button` updated to version `0.1.0`
|
|
10
|
+
* `mui-divider` updated to version `0.1.0`
|
|
11
|
+
* `mui-form-utils` updated to version `0.1.0`
|
|
12
|
+
* `mui-icon` updated to version `0.1.0`
|
|
13
|
+
* `mui-layout` updated to version `0.1.0`
|
|
14
|
+
* `mui-list` updated to version `0.1.0`
|
|
15
|
+
* `mui-progress` updated to version `0.1.0`
|
|
16
|
+
* `mui-typography` updated to version `0.1.0`
|
|
17
|
+
* `mui-paper` updated to version `0.1.0`
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* add FileSelector ([7ad1f7b](https://github.com/Availity/element/commit/7ad1f7bb364bbeb2048d2ff4c9b0a2b1a1e33777))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* **mui-file-selector:** update types ([477c992](https://github.com/Availity/element/commit/477c9921792ef35302b1b21d17056d16434c7d98))
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @availity/mui-file-selector
|
|
2
|
+
|
|
3
|
+
> Availity MUI File Selector component to be used with @availity/element design system.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@availity/mui-file-selector)
|
|
6
|
+
[](https://www.npmjs.com/package/@availity/mui-file-selector)
|
|
7
|
+
[](https://github.com/Availity/element/blob/main/packages/mui-file-selector/package.json)
|
|
8
|
+
|
|
9
|
+
## Documentation
|
|
10
|
+
|
|
11
|
+
This package extends the MUI File Selector component: [MUI File Selector Docs](https://mui.com/components/file-selector/)
|
|
12
|
+
|
|
13
|
+
Live demo and documentation in our [Storybook](https://availity.github.io/element/?path=/docs/components-file-selector-introduction--docs)
|
|
14
|
+
|
|
15
|
+
Availity standards for design and usage can be found in the [Availity Design Guide](https://zeroheight.com/2e36e50c7)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
### Import Through @availity/element (Recommended)
|
|
20
|
+
|
|
21
|
+
#### NPM
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @availity/element
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
#### Yarn
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
yarn add @availity/element
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Direct Import
|
|
34
|
+
|
|
35
|
+
#### NPM
|
|
36
|
+
|
|
37
|
+
_This package has a few peer dependencies. Add `@mui/material` & `@emotion/react` to your project if not already installed._
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @availity/mui-file-selector
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### Yarn
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
yarn add @availity/mui-file-selector
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Usage
|
|
50
|
+
|
|
51
|
+
#### Import through @availity/element
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { FileSelector } from '@availity/element';
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Direct import
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { FileSelector } from '@availity/mui-file-selector';
|
|
61
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import Upload from '@availity/upload-core';
|
|
3
|
+
|
|
4
|
+
type UploadProgressBarProps = {
|
|
5
|
+
/** The upload instance returned by creating a new Upload via @availity/upload-core. */
|
|
6
|
+
upload: Upload;
|
|
7
|
+
/** Callback function to hook into the onProgress within the Upload instance provided in the upload prop. */
|
|
8
|
+
onProgress?: (upload: Upload) => void;
|
|
9
|
+
/** Callback function to hook into the onSuccess within the Upload instance provided in the upload prop. */
|
|
10
|
+
onSuccess?: (upload: Upload) => void;
|
|
11
|
+
/** Callback function to hook into the onError within the Upload instance provided in the upload prop. */
|
|
12
|
+
onError?: (upload: Upload) => void;
|
|
13
|
+
};
|
|
14
|
+
declare const UploadProgressBar: ({ upload, onProgress, onError, onSuccess }: UploadProgressBarProps) => react_jsx_runtime.JSX.Element;
|
|
15
|
+
|
|
16
|
+
export { UploadProgressBar, type UploadProgressBarProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import Upload from '@availity/upload-core';
|
|
3
|
+
|
|
4
|
+
type UploadProgressBarProps = {
|
|
5
|
+
/** The upload instance returned by creating a new Upload via @availity/upload-core. */
|
|
6
|
+
upload: Upload;
|
|
7
|
+
/** Callback function to hook into the onProgress within the Upload instance provided in the upload prop. */
|
|
8
|
+
onProgress?: (upload: Upload) => void;
|
|
9
|
+
/** Callback function to hook into the onSuccess within the Upload instance provided in the upload prop. */
|
|
10
|
+
onSuccess?: (upload: Upload) => void;
|
|
11
|
+
/** Callback function to hook into the onError within the Upload instance provided in the upload prop. */
|
|
12
|
+
onError?: (upload: Upload) => void;
|
|
13
|
+
};
|
|
14
|
+
declare const UploadProgressBar: ({ upload, onProgress, onError, onSuccess }: UploadProgressBarProps) => react_jsx_runtime.JSX.Element;
|
|
15
|
+
|
|
16
|
+
export { UploadProgressBar, type UploadProgressBarProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
UploadProgressBar: () => UploadProgressBar
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(src_exports);
|
|
26
|
+
|
|
27
|
+
// src/lib/UploadProgressBar.tsx
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
var import_mui_progress = require("@availity/mui-progress");
|
|
30
|
+
var import_mui_typography = require("@availity/mui-typography");
|
|
31
|
+
var import_mui_icon = require("@availity/mui-icon");
|
|
32
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
33
|
+
var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
|
|
34
|
+
const [statePercentage, setStatePercentage] = (0, import_react.useState)(upload.percentage || 0);
|
|
35
|
+
const [error, setError] = (0, import_react.useState)(false);
|
|
36
|
+
const handleOnProgress = () => {
|
|
37
|
+
setStatePercentage(upload.percentage);
|
|
38
|
+
setError(false);
|
|
39
|
+
if (onProgress)
|
|
40
|
+
onProgress(upload);
|
|
41
|
+
};
|
|
42
|
+
const handleOnError = () => {
|
|
43
|
+
setError(true);
|
|
44
|
+
if (onError)
|
|
45
|
+
onError(upload);
|
|
46
|
+
};
|
|
47
|
+
const handleOnSuccess = () => {
|
|
48
|
+
setStatePercentage(100);
|
|
49
|
+
setError(false);
|
|
50
|
+
if (onSuccess)
|
|
51
|
+
onSuccess(upload);
|
|
52
|
+
};
|
|
53
|
+
upload.onProgress.push(handleOnProgress);
|
|
54
|
+
upload.onSuccess.push(handleOnSuccess);
|
|
55
|
+
upload.onError.push(handleOnError);
|
|
56
|
+
return upload.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_mui_typography.Typography, { color: "text.error", children: [
|
|
57
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_mui_icon.WarningTriangleIcon, {}),
|
|
58
|
+
" ",
|
|
59
|
+
upload.errorMessage
|
|
60
|
+
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_mui_progress.LinearProgress, { value: statePercentage, "aria-label": `${upload.file.name}-progress` });
|
|
61
|
+
};
|
|
62
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
63
|
+
0 && (module.exports = {
|
|
64
|
+
UploadProgressBar
|
|
65
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/lib/UploadProgressBar.tsx
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { LinearProgress } from "@availity/mui-progress";
|
|
4
|
+
import { Typography } from "@availity/mui-typography";
|
|
5
|
+
import { WarningTriangleIcon } from "@availity/mui-icon";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
|
|
8
|
+
const [statePercentage, setStatePercentage] = useState(upload.percentage || 0);
|
|
9
|
+
const [error, setError] = useState(false);
|
|
10
|
+
const handleOnProgress = () => {
|
|
11
|
+
setStatePercentage(upload.percentage);
|
|
12
|
+
setError(false);
|
|
13
|
+
if (onProgress)
|
|
14
|
+
onProgress(upload);
|
|
15
|
+
};
|
|
16
|
+
const handleOnError = () => {
|
|
17
|
+
setError(true);
|
|
18
|
+
if (onError)
|
|
19
|
+
onError(upload);
|
|
20
|
+
};
|
|
21
|
+
const handleOnSuccess = () => {
|
|
22
|
+
setStatePercentage(100);
|
|
23
|
+
setError(false);
|
|
24
|
+
if (onSuccess)
|
|
25
|
+
onSuccess(upload);
|
|
26
|
+
};
|
|
27
|
+
upload.onProgress.push(handleOnProgress);
|
|
28
|
+
upload.onSuccess.push(handleOnSuccess);
|
|
29
|
+
upload.onError.push(handleOnError);
|
|
30
|
+
return upload.errorMessage ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Typography, { color: "text.error", children: [
|
|
31
|
+
/* @__PURE__ */ jsx(WarningTriangleIcon, {}),
|
|
32
|
+
" ",
|
|
33
|
+
upload.errorMessage
|
|
34
|
+
] }) }) : /* @__PURE__ */ jsx(LinearProgress, { value: statePercentage, "aria-label": `${upload.file.name}-progress` });
|
|
35
|
+
};
|
|
36
|
+
export {
|
|
37
|
+
UploadProgressBar
|
|
38
|
+
};
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@availity/mui-file-selector",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Availity MUI file-selector Component - part of the @availity/element design system",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"typescript",
|
|
8
|
+
"availity",
|
|
9
|
+
"mui"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://availity.github.io/element/?path=/docs/components-file-selector-introduction--docs",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Availity/element/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/Availity/element.git",
|
|
18
|
+
"directory": "packages/file-selector"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Availity Developers <AVOSS@availity.com>",
|
|
22
|
+
"browser": "./dist/index.js",
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"module": "./dist/index.mjs",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
28
|
+
"dev": "tsup src/index.ts --format esm,cjs --watch --dts",
|
|
29
|
+
"clean": "rm -rf dist",
|
|
30
|
+
"clean:nm": "rm -rf node_modules",
|
|
31
|
+
"publish": "yarn npm publish --tolerate-republish --access public",
|
|
32
|
+
"publish:canary": "yarn npm publish --access public --tag canary"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@availity/api-axios": "^9.0.4",
|
|
36
|
+
"@availity/mui-button": "^0.6.11",
|
|
37
|
+
"@availity/mui-divider": "^0.4.0",
|
|
38
|
+
"@availity/mui-form-utils": "^0.14.0",
|
|
39
|
+
"@availity/mui-icon": "^0.11.0",
|
|
40
|
+
"@availity/mui-layout": "^0.2.0",
|
|
41
|
+
"@availity/mui-list": "^0.2.0",
|
|
42
|
+
"@availity/mui-progress": "^0.4.0",
|
|
43
|
+
"@availity/mui-typography": "^0.2.1",
|
|
44
|
+
"@availity/upload-core": "^6.1.1",
|
|
45
|
+
"@tanstack/react-query": "^4.36.1",
|
|
46
|
+
"react-dropzone": "^11.7.1",
|
|
47
|
+
"react-hook-form": "^7.51.3",
|
|
48
|
+
"uuid": "^9.0.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@mui/material": "^5.15.15",
|
|
52
|
+
"@types/tus-js-client": "^1.8.0",
|
|
53
|
+
"react": "18.2.0",
|
|
54
|
+
"react-dom": "18.2.0",
|
|
55
|
+
"tsup": "^8.0.2",
|
|
56
|
+
"typescript": "^5.4.5"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@mui/material": "^5.11.9",
|
|
60
|
+
"react": ">=16.3.0"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/project.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mui-file-selector",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "packages/file-selector/src",
|
|
5
|
+
"projectType": "library",
|
|
6
|
+
"tags": [],
|
|
7
|
+
"targets": {
|
|
8
|
+
"lint": {
|
|
9
|
+
"executor": "@nx/eslint:lint",
|
|
10
|
+
"options": {
|
|
11
|
+
"eslintConfig": ".eslintrc.json",
|
|
12
|
+
"silent": false,
|
|
13
|
+
"fix": false,
|
|
14
|
+
"cache": true,
|
|
15
|
+
"cacheLocation": "./node_modules/.cache/file-selector/.eslintcache",
|
|
16
|
+
"maxWarnings": -1,
|
|
17
|
+
"quiet": false,
|
|
18
|
+
"noEslintrc": false,
|
|
19
|
+
"hasTypeAwareRules": true,
|
|
20
|
+
"cacheStrategy": "metadata"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"test": {
|
|
24
|
+
"executor": "@nx/jest:jest",
|
|
25
|
+
"outputs": ["{workspaceRoot}/coverage/file-selector"],
|
|
26
|
+
"options": {
|
|
27
|
+
"jestConfig": "packages/file-selector/jest.config.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"version": {
|
|
31
|
+
"executor": "@jscutlery/semver:version",
|
|
32
|
+
"options": {
|
|
33
|
+
"preset": "conventional",
|
|
34
|
+
"commitMessageFormat": "chore({projectName}): release version ${version} [skip ci]",
|
|
35
|
+
"tagPrefix": "@availity/{projectName}@",
|
|
36
|
+
"trackDeps": true,
|
|
37
|
+
"skipCommitTypes": ["docs"]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lib/UploadProgressBar';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
4
|
+
import { useForm, FormProvider } from 'react-hook-form';
|
|
5
|
+
|
|
6
|
+
import { Dropzone } from './Dropzone';
|
|
7
|
+
|
|
8
|
+
const TestForm = ({ children }: { children: ReactNode }) => {
|
|
9
|
+
const methods = useForm();
|
|
10
|
+
|
|
11
|
+
return <FormProvider {...methods}>{children}</FormProvider>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe('Dropzone', () => {
|
|
15
|
+
test('should render successfully', () => {
|
|
16
|
+
const client = new QueryClient();
|
|
17
|
+
|
|
18
|
+
render(
|
|
19
|
+
<QueryClientProvider client={client}>
|
|
20
|
+
<TestForm>
|
|
21
|
+
<Dropzone name="test" bucketId="test" customerId="123" clientId="test" maxSize={1000} />
|
|
22
|
+
</TestForm>
|
|
23
|
+
</QueryClientProvider>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(screen.getByText('Drag and Drop Files Here')).toBeTruthy();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { ChangeEvent, MouseEvent, useCallback, useState } from 'react';
|
|
2
|
+
import { useDropzone, FileRejection, DropEvent } from 'react-dropzone';
|
|
3
|
+
import { v4 as uuid } from 'uuid';
|
|
4
|
+
import { Divider } from '@availity/mui-divider';
|
|
5
|
+
import { CloudDownloadIcon } from '@availity/mui-icon';
|
|
6
|
+
import { Box, Stack } from '@availity/mui-layout';
|
|
7
|
+
import { Typography } from '@availity/mui-typography';
|
|
8
|
+
import Upload, { Options } from '@availity/upload-core';
|
|
9
|
+
|
|
10
|
+
import { FilePickerBtn } from './FilePickerBtn';
|
|
11
|
+
|
|
12
|
+
const outerBoxStyles = {
|
|
13
|
+
backgroundColor: 'background.canvas',
|
|
14
|
+
border: '1px dotted',
|
|
15
|
+
borderRadius: '4px',
|
|
16
|
+
padding: '2rem',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const innerBoxStyles = {
|
|
20
|
+
width: '100%',
|
|
21
|
+
height: '100%',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const CLOUD_URL = '/cloud/web/appl/vault/upload/v1/resumable';
|
|
25
|
+
|
|
26
|
+
export type DropzoneProps = {
|
|
27
|
+
name: string;
|
|
28
|
+
bucketId: string;
|
|
29
|
+
clientId: string;
|
|
30
|
+
customerId: string;
|
|
31
|
+
allowedFileNameCharacters?: string;
|
|
32
|
+
allowedFileTypes?: `.${string}`[];
|
|
33
|
+
deliverFileOnSubmit?: boolean;
|
|
34
|
+
deliveryChannel?: string;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
endpoint?: string;
|
|
37
|
+
fileDeliveryMetadata?: Record<string, unknown> | ((file: Upload) => Record<string, unknown>);
|
|
38
|
+
getDropRejectionMessages?: (fileRejectsions: FileRejection[]) => void;
|
|
39
|
+
isCloud?: boolean;
|
|
40
|
+
maxFiles?: number;
|
|
41
|
+
maxSize?: number;
|
|
42
|
+
multiple?: boolean;
|
|
43
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
44
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
45
|
+
// onDeliveryError?: (responses: unknown[]) => void;
|
|
46
|
+
// onDeliverySuccess?: (responses: unknown[]) => void;
|
|
47
|
+
onFileDelivery?: (upload: Upload) => void;
|
|
48
|
+
onFilePreUpload?: ((upload: Upload) => boolean)[];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const Dropzone = ({
|
|
52
|
+
allowedFileNameCharacters,
|
|
53
|
+
allowedFileTypes = [],
|
|
54
|
+
bucketId,
|
|
55
|
+
clientId,
|
|
56
|
+
customerId,
|
|
57
|
+
deliveryChannel,
|
|
58
|
+
// deliverFileOnSubmit,
|
|
59
|
+
fileDeliveryMetadata,
|
|
60
|
+
disabled,
|
|
61
|
+
endpoint,
|
|
62
|
+
getDropRejectionMessages,
|
|
63
|
+
isCloud,
|
|
64
|
+
maxFiles,
|
|
65
|
+
maxSize,
|
|
66
|
+
multiple,
|
|
67
|
+
name,
|
|
68
|
+
onChange,
|
|
69
|
+
onClick,
|
|
70
|
+
onFilePreUpload,
|
|
71
|
+
onFileDelivery,
|
|
72
|
+
}: DropzoneProps) => {
|
|
73
|
+
const [totalSize, setTotalSize] = useState(0);
|
|
74
|
+
const [files, setFiles] = useState<Upload[]>([]);
|
|
75
|
+
|
|
76
|
+
const onDrop = useCallback(
|
|
77
|
+
(acceptedFiles: File[], fileRejections: FileRejection[], dropEvent: DropEvent) => {
|
|
78
|
+
// Do something with the files
|
|
79
|
+
console.log('Dropzone acceptedFiles:', acceptedFiles);
|
|
80
|
+
console.log('Dropzone fileRejections:', fileRejections);
|
|
81
|
+
console.log('Dropzone dropEvent:', dropEvent);
|
|
82
|
+
|
|
83
|
+
// Verify we have not exceeded max number of files
|
|
84
|
+
if (maxFiles && acceptedFiles.length > maxFiles) {
|
|
85
|
+
acceptedFiles.slice(0, Math.max(9, maxFiles));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const uploads = acceptedFiles.map((file) => {
|
|
89
|
+
const options: Options = {
|
|
90
|
+
bucketId,
|
|
91
|
+
customerId,
|
|
92
|
+
clientId,
|
|
93
|
+
fileTypes: allowedFileTypes,
|
|
94
|
+
maxSize,
|
|
95
|
+
allowedFileNameCharacters,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (onFilePreUpload) options.onPreStart = onFilePreUpload;
|
|
99
|
+
if (endpoint) options.endpoint = endpoint;
|
|
100
|
+
if (isCloud) options.endpoint = CLOUD_URL;
|
|
101
|
+
|
|
102
|
+
const upload = new Upload(file, options);
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
104
|
+
// @ts-ignore
|
|
105
|
+
upload.id = `${upload.id}-${uuid()}`;
|
|
106
|
+
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
108
|
+
// @ts-ignore
|
|
109
|
+
if (file.dropRejectionMessage) {
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
111
|
+
// @ts-ignore
|
|
112
|
+
upload.errorMessage = file.dropRejectionMessage;
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
114
|
+
// @ts-ignore
|
|
115
|
+
} else if (maxSize && totalSize + newFilesTotalSize + upload.file.size > maxSize) {
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
117
|
+
// @ts-ignore
|
|
118
|
+
upload.errorMessage = 'Total documents size is too large';
|
|
119
|
+
} else {
|
|
120
|
+
upload.start();
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
122
|
+
// @ts-ignore
|
|
123
|
+
newFilesTotalSize += upload.file.size;
|
|
124
|
+
}
|
|
125
|
+
if (onFileDelivery) {
|
|
126
|
+
onFileDelivery(upload);
|
|
127
|
+
} else if (deliveryChannel && fileDeliveryMetadata) {
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
129
|
+
// @ts-ignore
|
|
130
|
+
// upload.onSuccess.push(() => {
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
132
|
+
// @ts-ignore
|
|
133
|
+
// if (upload?.references?.[0]) {
|
|
134
|
+
// allow form to revalidate when upload is complete
|
|
135
|
+
// setFieldTouched(name, true);
|
|
136
|
+
// deliver upon upload complete, not form submit
|
|
137
|
+
// if (!deliverFileOnSubmit) {
|
|
138
|
+
// callFileDelivery(upload);
|
|
139
|
+
// }
|
|
140
|
+
// }
|
|
141
|
+
// });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return upload;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Set uploads somewhere. state?
|
|
148
|
+
setFiles(files);
|
|
149
|
+
|
|
150
|
+
if (getDropRejectionMessages) getDropRejectionMessages(fileRejections);
|
|
151
|
+
},
|
|
152
|
+
[getDropRejectionMessages]
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const { getRootProps, getInputProps } = useDropzone({ onDrop });
|
|
156
|
+
|
|
157
|
+
const accept = allowedFileTypes.join(',');
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<Box sx={outerBoxStyles} {...getRootProps()}>
|
|
161
|
+
<Box sx={innerBoxStyles}>
|
|
162
|
+
<Stack spacing={2} divider={<Divider>OR</Divider>} alignItems="center" justifyContent="center">
|
|
163
|
+
<>
|
|
164
|
+
<CloudDownloadIcon fontSize="xlarge" color="secondary" />
|
|
165
|
+
<Typography variant="subtitle2" fontWeight="700">
|
|
166
|
+
Drag and Drop Files Here
|
|
167
|
+
</Typography>
|
|
168
|
+
<FilePickerBtn
|
|
169
|
+
name={name}
|
|
170
|
+
color="primary"
|
|
171
|
+
disabled={disabled}
|
|
172
|
+
maxSize={maxSize}
|
|
173
|
+
onClick={onClick}
|
|
174
|
+
inputProps={getInputProps({
|
|
175
|
+
multiple,
|
|
176
|
+
accept,
|
|
177
|
+
onChange,
|
|
178
|
+
})}
|
|
179
|
+
/>
|
|
180
|
+
</>
|
|
181
|
+
</Stack>
|
|
182
|
+
</Box>
|
|
183
|
+
</Box>
|
|
184
|
+
);
|
|
185
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
|
|
3
|
+
import { FileList } from './FileList';
|
|
4
|
+
|
|
5
|
+
describe('FileList', () => {
|
|
6
|
+
test('should render successfully', () => {
|
|
7
|
+
render(
|
|
8
|
+
<FileList
|
|
9
|
+
uploads={[]}
|
|
10
|
+
onRemoveFile={() => {
|
|
11
|
+
// noop
|
|
12
|
+
}}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type Upload from '@availity/upload-core';
|
|
2
|
+
import { List, ListItem, ListItemText, ListItemIcon, ListItemButton } from '@availity/mui-list';
|
|
3
|
+
import { DeleteIcon, FileIcon } from '@availity/mui-icon';
|
|
4
|
+
import { Grid } from '@availity/mui-layout';
|
|
5
|
+
|
|
6
|
+
import { UploadProgressBar } from './UploadProgressBar';
|
|
7
|
+
import { formatBytes, getFileExtIcon } from './util';
|
|
8
|
+
|
|
9
|
+
type FileRowProps = {
|
|
10
|
+
/** The upload instance returned by creating a new Upload via @availity/upload-core. */
|
|
11
|
+
upload: Upload;
|
|
12
|
+
/** Callback called when file is removed. The callback is passed the id of the file that was removed. */
|
|
13
|
+
onRemoveFile: (id: string) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const FileRow = ({ upload, onRemoveFile }: FileRowProps) => {
|
|
17
|
+
const { ext, icon } = getFileExtIcon(upload.file.name);
|
|
18
|
+
console.log('ext, icon:', ext, icon);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<ListItem>
|
|
22
|
+
<Grid container spacing={2} alignItems="center" justifyContent="space-between" width="100%">
|
|
23
|
+
<Grid xs={1}>
|
|
24
|
+
<ListItemIcon>
|
|
25
|
+
<FileIcon />
|
|
26
|
+
</ListItemIcon>
|
|
27
|
+
</Grid>
|
|
28
|
+
<Grid xs={4}>
|
|
29
|
+
<ListItemText>{upload.trimFileName(upload.file.name)}</ListItemText>
|
|
30
|
+
</Grid>
|
|
31
|
+
<Grid xs={2}>
|
|
32
|
+
<ListItem>{formatBytes(upload.file.size)}</ListItem>
|
|
33
|
+
</Grid>
|
|
34
|
+
<Grid xs={4}>
|
|
35
|
+
<UploadProgressBar upload={upload} />
|
|
36
|
+
</Grid>
|
|
37
|
+
<Grid xs={1}>
|
|
38
|
+
<ListItemButton>
|
|
39
|
+
<ListItemIcon
|
|
40
|
+
onClick={() => {
|
|
41
|
+
onRemoveFile(upload.id);
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<DeleteIcon />
|
|
45
|
+
</ListItemIcon>
|
|
46
|
+
</ListItemButton>
|
|
47
|
+
</Grid>
|
|
48
|
+
</Grid>
|
|
49
|
+
</ListItem>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type FileListProps = {
|
|
54
|
+
/** List of Upload objects */
|
|
55
|
+
uploads: Upload[];
|
|
56
|
+
/** Callback called when file is removed. The callback is passed the id of the file that was removed. */
|
|
57
|
+
onRemoveFile: (id: string) => void;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const FileList = ({ uploads, onRemoveFile }: FileListProps) => {
|
|
61
|
+
if (uploads.length === 0) return null;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<List>
|
|
65
|
+
{uploads.map((upload) => {
|
|
66
|
+
return <FileRow key={upload.id} upload={upload} onRemoveFile={onRemoveFile} />;
|
|
67
|
+
})}
|
|
68
|
+
</List>
|
|
69
|
+
);
|
|
70
|
+
};
|