From 03fa8dab35204144f8e85d091b405e19caabed21 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sat, 28 Aug 2021 22:15:21 +0700 Subject: [PATCH 001/140] Configure for using hook in development --- rollup.config.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 1422490..043b41d 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,7 +2,7 @@ import typescript from 'rollup-plugin-typescript2'; import babel from '@rollup/plugin-babel'; import commonjs from '@rollup/plugin-commonjs'; import resolve from '@rollup/plugin-node-resolve'; -import builtins from 'builtin-modules' +// import builtins from 'builtin-modules' import { terser } from "rollup-plugin-terser"; import pkg from './package.json'; @@ -35,7 +35,10 @@ export default { // }, // }, ], - external: builtins, + // external: builtins, + // To use hook in development + // https://reactjs.org/warnings/invalid-hook-call-warning.html + external: ['react', 'react-dom'], plugins: [ typescript({ tsconfig: './tsconfig.json', From b501fa9714ae0b6314c85c858c9e180f807412b7 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 8 Sep 2021 21:51:50 +0700 Subject: [PATCH 002/140] Define readString in usePapaParse --- src/react-papaparse.ts | 1 + src/usePapaParse.tsx | 7 +++++++ supports/create-next-app/pages/index.tsx | 12 +++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/usePapaParse.tsx diff --git a/src/react-papaparse.ts b/src/react-papaparse.ts index deeec98..eee01ae 100644 --- a/src/react-papaparse.ts +++ b/src/react-papaparse.ts @@ -17,3 +17,4 @@ export { export { readString } from './readString'; export { readRemoteFile } from './readRemoteFile'; export { jsonToCSV } from './jsonToCSV'; +export { usePapaParse } from './usePapaParse'; diff --git a/src/usePapaParse.tsx b/src/usePapaParse.tsx new file mode 100644 index 0000000..29f6b1d --- /dev/null +++ b/src/usePapaParse.tsx @@ -0,0 +1,7 @@ +import { readString } from './readString'; + +export function usePapaParse() { + return { + readString, + }; +} diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 55c9a58..0c90c2b 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,7 +1,8 @@ import React, { useState } from 'react' -import { CSVReader, CSVDownloader } from 'react-papaparse' +import { CSVReader, CSVDownloader, usePapaParse } from 'react-papaparse' export default function Home() { + const { readString } = usePapaParse(); const [isReset, setIsReset] = useState(false); const handleReset = () => { @@ -29,6 +30,15 @@ export default function Home() { console.log('---------------------------') } + const str = `Column 1,Column 2,Column 3,Column 4 +1-1,1-2,1-3,1-4 +2-1,2-2,2-3,2-4 +3-1,3-2,3-3,3-4 +4,5,6,7` + + const results = readString(str) + console.log(results); + return ( <> Date: Wed, 8 Sep 2021 22:13:54 +0700 Subject: [PATCH 003/140] Define readRemoteFile in usePapaParse --- src/usePapaParse.tsx | 2 ++ supports/create-next-app/pages/index.tsx | 12 +----------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/usePapaParse.tsx b/src/usePapaParse.tsx index 29f6b1d..2d32744 100644 --- a/src/usePapaParse.tsx +++ b/src/usePapaParse.tsx @@ -1,7 +1,9 @@ import { readString } from './readString'; +import { readRemoteFile } from './readRemoteFile'; export function usePapaParse() { return { readString, + readRemoteFile, }; } diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 0c90c2b..55c9a58 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,8 +1,7 @@ import React, { useState } from 'react' -import { CSVReader, CSVDownloader, usePapaParse } from 'react-papaparse' +import { CSVReader, CSVDownloader } from 'react-papaparse' export default function Home() { - const { readString } = usePapaParse(); const [isReset, setIsReset] = useState(false); const handleReset = () => { @@ -30,15 +29,6 @@ export default function Home() { console.log('---------------------------') } - const str = `Column 1,Column 2,Column 3,Column 4 -1-1,1-2,1-3,1-4 -2-1,2-2,2-3,2-4 -3-1,3-2,3-3,3-4 -4,5,6,7` - - const results = readString(str) - console.log(results); - return ( <> Date: Wed, 8 Sep 2021 22:17:55 +0700 Subject: [PATCH 004/140] Define jsonToCSV in usePapaParse --- src/usePapaParse.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/usePapaParse.tsx b/src/usePapaParse.tsx index 2d32744..85236f0 100644 --- a/src/usePapaParse.tsx +++ b/src/usePapaParse.tsx @@ -1,9 +1,11 @@ import { readString } from './readString'; import { readRemoteFile } from './readRemoteFile'; +import { jsonToCSV } from './jsonToCSV'; export function usePapaParse() { return { readString, readRemoteFile, + jsonToCSV, }; } From 3c20d7efab520db4033f1ff304ca970b0388de9d Mon Sep 17 00:00:00 2001 From: exaucae Date: Thu, 14 Oct 2021 17:55:31 +0000 Subject: [PATCH 005/140] :sparkles: feat: add file encoding detection --- .gitignore | 4 +++ package.json | 1 + src/CSVReader.tsx | 92 +++++++++++++++++++++++++---------------------- 3 files changed, 55 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index 8e0924c..0b76271 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ node_modules build dist +# editors +.idea + # misc .DS_Store .env @@ -20,6 +23,7 @@ yarn-debug.log* yarn-error.log* package-lock.json +yarn.lock /docs/node_modules /docs/.next diff --git a/package.json b/package.json index 088cc54..a7d46c4 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "eslint": "^7.32.0", "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^4.2.0", + "jschardet": "^3.0.0", "jest": "^27.1.0", "mutationobserver-shim": "^0.3.7", "prettier": "^2.3.2", diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index ae34fbf..70ee908 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -3,6 +3,7 @@ import PapaParse, { ParseConfig, ParseResult } from 'papaparse'; import getSize, { lightenDarkenColor } from './utils'; import RemoveIcon from './RemoveIcon'; import ProgressBar from './ProgressBar'; +import jschardet from 'jschardet'; const GREY = '#CCC'; const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; @@ -113,10 +114,8 @@ interface State { isCanceled: boolean; } -export default class CSVReader extends React.Component< - Props, - State -> { +export default class CSVReader extends React.Component, + State> { static defaultProps: Partial> = { isReset: false, }; @@ -226,14 +225,14 @@ export default class CSVReader extends React.Component< ? style?.dropAreaActive : Object.assign({}, style?.dropAreaActive, styles.highlight) : style?.dropArea?.dropAreaActive - ? style?.dropArea?.dropAreaActive.borderColor - ? style?.dropArea?.dropAreaActive - : Object.assign( + ? style?.dropArea?.dropAreaActive.borderColor + ? style?.dropArea?.dropAreaActive + : Object.assign( {}, style?.dropArea?.dropAreaActive, styles.highlight, ) - : styles.highlight, + : styles.highlight, ), }); this.setState({ progressBar: 0 }); @@ -304,36 +303,36 @@ export default class CSVReader extends React.Component< config?.complete || config?.step ? config.complete : () => { + if (!onDrop && onFileLoad) { + onFileLoad(data, file); + } else if (onDrop && !onFileLoad) { + onDrop(data, file); + } + }, + step: config?.step + ? config.step + : (row: any) => { + data.push(row); + if (config && config.preview) { + percent = Math.round((data.length / config.preview) * 100); + self.setState({ progressBar: percent }); + if (data.length === config.preview) { if (!onDrop && onFileLoad) { onFileLoad(data, file); } else if (onDrop && !onFileLoad) { onDrop(data, file); } - }, - step: config?.step - ? config.step - : (row: any) => { - data.push(row); - if (config && config.preview) { - percent = Math.round((data.length / config.preview) * 100); - self.setState({ progressBar: percent }); - if (data.length === config.preview) { - if (!onDrop && onFileLoad) { - onFileLoad(data, file); - } else if (onDrop && !onFileLoad) { - onDrop(data, file); - } - } - } else { - const progress = row.meta.cursor; - const newPercent = Math.round((progress / size) * 100); - if (newPercent === percent) { - return; - } - percent = newPercent; } - self.setState({ progressBar: percent }); - }, + } else { + const progress = row.meta.cursor; + const newPercent = Math.round((progress / size) * 100); + if (newPercent === percent) { + return; + } + percent = newPercent; + } + self.setState({ progressBar: percent }); + }, }, options, ); @@ -348,6 +347,11 @@ export default class CSVReader extends React.Component< } reader.onload = (e: any) => { + + if (!config.encoding) { + config.encoding = jschardet.detect(e.target.result).encoding + options = Object.assign({}, config, options); + } PapaParse.parse(e.target.result, options); }; @@ -359,8 +363,12 @@ export default class CSVReader extends React.Component< }, 2000), }); }; - + if(config?.encoding){ reader.readAsText(file, config.encoding || 'utf-8'); + } + else { + reader.readAsBinaryString(file); + } }; displayFileInfo = (file: any) => { @@ -482,7 +490,7 @@ export default class CSVReader extends React.Component< return ( <> extends React.Component< {}, styles.fileSizeInfo, style?.dropArea?.dropFile?.fileSizeInfo || - style?.dropArea?.fileSizeInfo || - style?.fileSizeInfo, + style?.dropArea?.fileSizeInfo || + style?.fileSizeInfo, )} ref={this.fileSizeInfoRef} /> @@ -532,8 +540,8 @@ export default class CSVReader extends React.Component< {}, styles.fileNameInfo, style?.dropArea?.dropFile?.fileNameInfo || - style?.dropFile?.fileNameInfo || - style?.fileNameInfo, + style?.dropFile?.fileNameInfo || + style?.fileNameInfo, )} ref={this.fileNameInfoRef} /> @@ -547,8 +555,8 @@ export default class CSVReader extends React.Component< ? { backgroundColor: progressBarColor } : {}, style?.dropArea?.dropFile?.progressBar || - style?.dropFile?.progressBar || - style?.progressBar, + style?.dropFile?.progressBar || + style?.progressBar, )} progressBar={progressBar} displayProgressBarStatus={displayProgressBarStatus} @@ -569,8 +577,8 @@ export default class CSVReader extends React.Component< {}, progressBarColor ? { backgroundColor: progressBarColor } : {}, style?.dropArea?.dropFile?.progressBar || - style?.dropFile?.progressBar || - style?.progressBar, + style?.dropFile?.progressBar || + style?.progressBar, )} progressBar={progressBar} displayProgressBarStatus={displayProgressBarStatus} From afbd1170009286399ad84fa82412fa0f9f30d2a4 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 3 Nov 2021 10:16:36 +0700 Subject: [PATCH 006/140] Add .github --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..a663eb5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: bunlong From 00917ac59e7bb2dcab481568132559a15cd8e4a7 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 3 Nov 2021 15:19:32 +0700 Subject: [PATCH 007/140] Fix types --- package.json | 2 +- src/CSVDownloader.tsx | 12 ++++++------ src/CSVReader.tsx | 2 +- src/readRemoteFile.ts | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 088cc54..a8c0da9 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "dist" ], "dependencies": { - "@types/papaparse": "^5.2.6", + "@types/papaparse": "^5.3.1", "papaparse": "^5.3.1" }, "bundlesize": [ diff --git a/src/CSVDownloader.tsx b/src/CSVDownloader.tsx index a7fb5e0..4e42051 100644 --- a/src/CSVDownloader.tsx +++ b/src/CSVDownloader.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import PapaParse, { ParseConfig } from 'papaparse'; +import PapaParse, { UnparseConfig } from 'papaparse'; export const LINK_TYPE = 'link'; export const BUTTON_TYPE = 'button'; -export interface Props { +export interface Props { children: React.ReactNode; data: any; filename: string; @@ -12,11 +12,11 @@ export interface Props { style?: any; className?: string; bom?: boolean; - config?: ParseConfig; + config?: UnparseConfig; } -export default class CSVDownloader extends React.Component> { - static defaultProps: Partial> = { +export default class CSVDownloader extends React.Component { + static defaultProps: Partial = { type: LINK_TYPE, }; @@ -25,7 +25,7 @@ export default class CSVDownloader extends React.Component> { data: any, filename: string, bom: boolean, - config: ParseConfig, + config: UnparseConfig, ): void => { const bomCode = bom ? '\ufeff' : ''; let csvContent = null; diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index ae34fbf..402e3ee 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -89,7 +89,7 @@ interface Props { onDrop?: (data: Array>, file?: any) => void; onFileLoad?: (data: Array>, file?: any) => void; onError?: (err: any, file: any, inputElem: any, reason: any) => void; - config?: ParseConfig; + config?: ParseConfig | any; style?: any; noClick?: boolean; noDrag?: boolean; diff --git a/src/readRemoteFile.ts b/src/readRemoteFile.ts index 9dd9531..d8ca722 100644 --- a/src/readRemoteFile.ts +++ b/src/readRemoteFile.ts @@ -1,5 +1,5 @@ -import PapaParse, { ParseConfig } from 'papaparse'; +import PapaParse, { ParseConfig, NODE_STREAM_INPUT } from 'papaparse'; -export function readRemoteFile(url: string, options: ParseConfig = {}) { +export function readRemoteFile(url: typeof NODE_STREAM_INPUT, options: ParseConfig = {}) { PapaParse.parse(url, Object.assign({}, { download: true }, options)); } From a52b59e5eef4c1ca572f8b93f0d44379af39fb46 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 4 Nov 2021 11:02:51 +0700 Subject: [PATCH 008/140] Fix miss encoding type --- src/CSVReader.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index 402e3ee..e1bd30d 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -84,12 +84,18 @@ const styles = { } as CSSProperties, }; +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/papaparse/index.d.ts +interface CustomConfig extends ParseConfig { + /** The encoding to use when opening local files. If specified, it must be a value supported by the FileReader API. */ + encoding?: string | undefined; +} + interface Props { children: any; onDrop?: (data: Array>, file?: any) => void; onFileLoad?: (data: Array>, file?: any) => void; onError?: (err: any, file: any, inputElem: any, reason: any) => void; - config?: ParseConfig | any; + config?: CustomConfig; style?: any; noClick?: boolean; noDrag?: boolean; From 0e8ef80fcbd6996114d7453e836afc0bf7cb454f Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Thu, 4 Nov 2021 00:24:12 -0400 Subject: [PATCH 009/140] Fix unmount issue with CSVReader Fix issue when component is unmounted immediately after file load in CSVReader --- src/CSVReader.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index ae34fbf..d5e0798 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -351,14 +351,16 @@ export default class CSVReader extends React.Component< PapaParse.parse(e.target.result, options); }; - reader.onloadend = () => { - clearTimeout(this.state.timeout); - this.setState({ - timeout: setTimeout(() => { - this.disableProgressBar(); - }, 2000), - }); - }; + if (!this.props.noProgressBar) { + reader.onloadend = () => { + clearTimeout(this.state.timeout); + this.setState({ + timeout: setTimeout(() => { + this.disableProgressBar(); + }, 2000), + }); + }; + } reader.readAsText(file, config.encoding || 'utf-8'); }; From 10427222d000a0c2ef972e009db1d7e9ff76e2b3 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 4 Nov 2021 11:24:41 +0700 Subject: [PATCH 010/140] Add reference --- src/CSVReader.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index e1bd30d..349fbfa 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -84,7 +84,8 @@ const styles = { } as CSSProperties, }; -// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/papaparse/index.d.ts +// 5.3 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/papaparse/index.d.ts +// 5.2 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d3737ebd9125505f7ea237b9f17f1426579a3917/types/papaparse/index.d.ts interface CustomConfig extends ParseConfig { /** The encoding to use when opening local files. If specified, it must be a value supported by the FileReader API. */ encoding?: string | undefined; From 34f8e1aacdf944c9992fa736dcc86d8c7ae89d23 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 4 Nov 2021 15:35:21 +0700 Subject: [PATCH 011/140] Create model interface --- src/CSVReader.tsx | 12 +++--------- src/model.tsx | 9 +++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 src/model.tsx diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index 349fbfa..30fcac2 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -1,5 +1,6 @@ import React, { CSSProperties } from 'react'; -import PapaParse, { ParseConfig, ParseResult } from 'papaparse'; +import PapaParse, { ParseResult } from 'papaparse'; +import { CSVReaderConfig } from './model'; import getSize, { lightenDarkenColor } from './utils'; import RemoveIcon from './RemoveIcon'; import ProgressBar from './ProgressBar'; @@ -84,19 +85,12 @@ const styles = { } as CSSProperties, }; -// 5.3 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/papaparse/index.d.ts -// 5.2 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d3737ebd9125505f7ea237b9f17f1426579a3917/types/papaparse/index.d.ts -interface CustomConfig extends ParseConfig { - /** The encoding to use when opening local files. If specified, it must be a value supported by the FileReader API. */ - encoding?: string | undefined; -} - interface Props { children: any; onDrop?: (data: Array>, file?: any) => void; onFileLoad?: (data: Array>, file?: any) => void; onError?: (err: any, file: any, inputElem: any, reason: any) => void; - config?: CustomConfig; + config?: CSVReaderConfig; style?: any; noClick?: boolean; noDrag?: boolean; diff --git a/src/model.tsx b/src/model.tsx new file mode 100644 index 0000000..9f9a3b7 --- /dev/null +++ b/src/model.tsx @@ -0,0 +1,9 @@ +import { ParseConfig } from 'papaparse'; + +// 5.3 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/papaparse/index.d.ts +// 5.2 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d3737ebd9125505f7ea237b9f17f1426579a3917/types/papaparse/index.d.ts + +export interface CSVReaderConfig extends ParseConfig { + /** The encoding to use when opening local files. If specified, it must be a value supported by the FileReader API. */ + encoding?: string | undefined; +} From 4ae6ae3ab22bd8bcad84aa3465df9b5c8cef94b8 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 4 Nov 2021 15:48:59 +0700 Subject: [PATCH 012/140] Create ReadRemoteFileConfig model --- src/model.tsx | 51 ++++++++++++++++++++++++++++++++++++++++++- src/readRemoteFile.ts | 5 +++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/model.tsx b/src/model.tsx index 9f9a3b7..c2f6b63 100644 --- a/src/model.tsx +++ b/src/model.tsx @@ -1,4 +1,4 @@ -import { ParseConfig } from 'papaparse'; +import { ParseConfig, ParseResult, Parser } from 'papaparse'; // 5.3 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/papaparse/index.d.ts // 5.2 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d3737ebd9125505f7ea237b9f17f1426579a3917/types/papaparse/index.d.ts @@ -7,3 +7,52 @@ export interface CSVReaderConfig extends ParseConfi /** The encoding to use when opening local files. If specified, it must be a value supported by the FileReader API. */ encoding?: string | undefined; } + +export interface ReadRemoteFileConfig extends ParseConfig { + /** + * Whether or not to use a worker thread. + * Using a worker will keep your page reactive, but may be slightly slower. + * @default false + */ + worker?: boolean | undefined; + /** + * Overrides `Papa.LocalChunkSize` and `Papa.RemoteChunkSize`. + */ + chunkSize?: number | undefined; + /** + * A callback function, identical to `step`, which activates streaming. + * However, this function is executed after every chunk of the file is loaded and parsed rather than every row. + * Works only with local and remote files. + * Do not use both `chunk` and `step` callbacks together. + */ + chunk?(results: ParseResult, parser: Parser): void; + /** + * A callback to execute if FileReader encounters an error. + * The function is passed two arguments: the error and the File. + */ + error?(error: Error, file: TInput): void; + /** @inheritdoc */ + complete(results: ParseResult, file: TInput): void; + + /** + * This indicates that the string you passed as the first argument to `parse()` + * is actually a URL from which to download a file and parse its contents. + */ + download: true; + /** + * If defined, should be an object that describes the headers. + * @example { 'Authorization': 'token 123345678901234567890' } + * @default undefined + */ + downloadRequestHeaders?: { [headerName: string]: string } | undefined; + /** + * Use POST request on the URL of the download option. The value passed will be set as the body of the request. + * @default undefined + */ + downloadRequestBody?: Blob | BufferSource | FormData | URLSearchParams | string | undefined; + /** + * A boolean value passed directly into XMLHttpRequest's "withCredentials" property. + * @default undefined + */ + withCredentials?: boolean | undefined; +} diff --git a/src/readRemoteFile.ts b/src/readRemoteFile.ts index d8ca722..aed6daf 100644 --- a/src/readRemoteFile.ts +++ b/src/readRemoteFile.ts @@ -1,5 +1,6 @@ -import PapaParse, { ParseConfig, NODE_STREAM_INPUT } from 'papaparse'; +import PapaParse, { NODE_STREAM_INPUT } from 'papaparse'; +import { ReadRemoteFileConfig } from './model'; -export function readRemoteFile(url: typeof NODE_STREAM_INPUT, options: ParseConfig = {}) { +export function readRemoteFile(url: typeof NODE_STREAM_INPUT, options: ReadRemoteFileConfig) { PapaParse.parse(url, Object.assign({}, { download: true }, options)); } From 8c9d296e85423d6a43f14ab3a4bbcfcb90958626 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 4 Nov 2021 17:33:40 +0700 Subject: [PATCH 013/140] Create CustomConfig model --- src/CSVReader.tsx | 4 ++-- src/model.tsx | 37 +++++++++++++++++++++++++++---------- src/readRemoteFile.ts | 7 +++++-- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index 30fcac2..627cbac 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -1,6 +1,6 @@ import React, { CSSProperties } from 'react'; import PapaParse, { ParseResult } from 'papaparse'; -import { CSVReaderConfig } from './model'; +import { CustomConfig } from './model'; import getSize, { lightenDarkenColor } from './utils'; import RemoveIcon from './RemoveIcon'; import ProgressBar from './ProgressBar'; @@ -90,7 +90,7 @@ interface Props { onDrop?: (data: Array>, file?: any) => void; onFileLoad?: (data: Array>, file?: any) => void; onError?: (err: any, file: any, inputElem: any, reason: any) => void; - config?: CSVReaderConfig; + config?: CustomConfig; style?: any; noClick?: boolean; noDrag?: boolean; diff --git a/src/model.tsx b/src/model.tsx index c2f6b63..b7971b4 100644 --- a/src/model.tsx +++ b/src/model.tsx @@ -3,18 +3,20 @@ import { ParseConfig, ParseResult, Parser } from 'papaparse'; // 5.3 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/papaparse/index.d.ts // 5.2 => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d3737ebd9125505f7ea237b9f17f1426579a3917/types/papaparse/index.d.ts -export interface CSVReaderConfig extends ParseConfig { - /** The encoding to use when opening local files. If specified, it must be a value supported by the FileReader API. */ - encoding?: string | undefined; -} +export interface CustomConfig + extends ParseConfig { + /** + * * * * * * * * * * + * ParseAsyncConfig + * * * * * * * * * * + */ -export interface ReadRemoteFileConfig extends ParseConfig { /** * Whether or not to use a worker thread. * Using a worker will keep your page reactive, but may be slightly slower. * @default false */ - worker?: boolean | undefined; + worker?: boolean | undefined; /** * Overrides `Papa.LocalChunkSize` and `Papa.RemoteChunkSize`. */ @@ -31,14 +33,23 @@ export interface ReadRemoteFileConfig extends Parse * The function is passed two arguments: the error and the File. */ error?(error: Error, file: TInput): void; - /** @inheritdoc */ - complete(results: ParseResult, file: TInput): void; + + // ParseLocalConfig + /** The encoding to use when opening local files. If specified, it must be a value supported by the FileReader API. */ + encoding?: string | undefined; + + /** + * * * * * * * * * * * + * ParseRemoteConfig + * * * * * * * * * * * + */ /** * This indicates that the string you passed as the first argument to `parse()` * is actually a URL from which to download a file and parse its contents. */ - download: true; + // download: true; + download?: boolean | true; // default: false /** * If defined, should be an object that describes the headers. * @example { 'Authorization': 'token 123345678901234567890' } @@ -49,7 +60,13 @@ export interface ReadRemoteFileConfig extends Parse * Use POST request on the URL of the download option. The value passed will be set as the body of the request. * @default undefined */ - downloadRequestBody?: Blob | BufferSource | FormData | URLSearchParams | string | undefined; + downloadRequestBody?: + | Blob + | BufferSource + | FormData + | URLSearchParams + | string + | undefined; /** * A boolean value passed directly into XMLHttpRequest's "withCredentials" property. * @default undefined diff --git a/src/readRemoteFile.ts b/src/readRemoteFile.ts index aed6daf..010067f 100644 --- a/src/readRemoteFile.ts +++ b/src/readRemoteFile.ts @@ -1,6 +1,9 @@ import PapaParse, { NODE_STREAM_INPUT } from 'papaparse'; -import { ReadRemoteFileConfig } from './model'; +import { CustomConfig } from './model'; -export function readRemoteFile(url: typeof NODE_STREAM_INPUT, options: ReadRemoteFileConfig) { +export function readRemoteFile( + url: typeof NODE_STREAM_INPUT, + options: CustomConfig, +) { PapaParse.parse(url, Object.assign({}, { download: true }, options)); } From 34e9e1c5754327b40cc96a66706740a6fff8f461 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 4 Nov 2021 17:41:07 +0700 Subject: [PATCH 014/140] Fix options --- src/readRemoteFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/readRemoteFile.ts b/src/readRemoteFile.ts index 010067f..3c0343e 100644 --- a/src/readRemoteFile.ts +++ b/src/readRemoteFile.ts @@ -5,5 +5,5 @@ export function readRemoteFile( url: typeof NODE_STREAM_INPUT, options: CustomConfig, ) { - PapaParse.parse(url, Object.assign({}, { download: true }, options)); + PapaParse.parse(url, options); } From b502a23c24da1156ac42a6aad725bef9429524b4 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 5 Nov 2021 08:39:17 +0700 Subject: [PATCH 015/140] Fix string type --- README.md | 4 ---- src/readRemoteFile.ts | 2 +- src/readString.ts | 8 ++++++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8545715..951857e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,3 @@ -

- ⭐️ Please support us by giving a star! Thanks! ⭐️ -

- # react-papaparse react-papaparse is the fastest in-browser CSV (or delimited text) parser for React. It is full of useful features such as CSVReader, CSVDownloader, readString, jsonToCSV, readRemoteFile, ... etc. diff --git a/src/readRemoteFile.ts b/src/readRemoteFile.ts index 3c0343e..da3f077 100644 --- a/src/readRemoteFile.ts +++ b/src/readRemoteFile.ts @@ -3,7 +3,7 @@ import { CustomConfig } from './model'; export function readRemoteFile( url: typeof NODE_STREAM_INPUT, - options: CustomConfig, + options: CustomConfig = {}, ) { PapaParse.parse(url, options); } diff --git a/src/readString.ts b/src/readString.ts index b3efeb3..7ce4695 100644 --- a/src/readString.ts +++ b/src/readString.ts @@ -1,5 +1,9 @@ -import PapaParse, { ParseConfig } from 'papaparse'; +import PapaParse, { NODE_STREAM_INPUT } from 'papaparse'; +import { CustomConfig } from './model'; -export function readString(str: string, options: ParseConfig = {}) { +export function readString( + str: typeof NODE_STREAM_INPUT, + options: CustomConfig = {}, +) { return PapaParse.parse(str, options); } From 0243fc5c9ef672a8f96a1d02df45985dd9da2094 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 5 Nov 2021 08:44:42 +0700 Subject: [PATCH 016/140] Update CHANGELOG --- CHANGELOG.md | 10 ++++++++++ README.md | 16 ++++++++++++++-- package.json | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b09c979..a6e8d9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 3.18.0 (2021-11-05) + +### ✨ Bugs + +* Wrong Typescript CSVDownloader config type + +Credits + +* Graveheart [@Graveheart](https://github.com/Graveheart) + ## 3.17.2 (2021-09-4) ### ✨ Features diff --git a/README.md b/README.md index 951857e..3856fc3 100644 --- a/README.md +++ b/README.md @@ -490,15 +490,16 @@ readRemoteFile('http://example.com/big.csv', { ## 📜 Changelog -Latest version 3.17.2 (2021-09-04): +Latest version 3.18.0 (2021-11-05): - * Upgrade dependencies + * Wrong Typescript CSVDownloader config type Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/react-papaparse/blob/master/CHANGELOG.md). ## 🛣️ Roadmap ### 🆕 v4.0.x + * Improve code performance * Rewrite any existing based components to hooks * CSVReader multiple files drag and drop @@ -747,6 +748,17 @@ How to contribute: + + + + Venelin Banov +
+ + Venelin Banov + +
+ + ## 👨‍👩‍👦 Advertisement diff --git a/package.json b/package.json index a8c0da9..a9eb1eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-papaparse", - "version": "3.17.2", + "version": "3.18.0", "description": "The fastest in-browser CSV (or delimited text) parser for React. It is full of useful features such as CSVReader, CSVDownloader, readString, jsonToCSV, readRemoteFile, ... etc.", "author": "Bunlong ", "license": "MIT", From 4d54d5bbc0791f4817c969d320dc5827b47bf454 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 5 Nov 2021 08:47:21 +0700 Subject: [PATCH 017/140] Set download --- src/readRemoteFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/readRemoteFile.ts b/src/readRemoteFile.ts index da3f077..61348e5 100644 --- a/src/readRemoteFile.ts +++ b/src/readRemoteFile.ts @@ -5,5 +5,5 @@ export function readRemoteFile( url: typeof NODE_STREAM_INPUT, options: CustomConfig = {}, ) { - PapaParse.parse(url, options); + PapaParse.parse(url, Object.assign({}, { download: true }, options)); } From 307191c26ebc18a4f64fae6aa15fb755173a875b Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 5 Nov 2021 10:27:27 +0700 Subject: [PATCH 018/140] Make prettier --- package.json | 4 ++-- src/readRemoteFile.ts | 10 +++------- src/readString.ts | 9 ++++----- test/jsonToCSV.spec.ts | 22 ++++++++++++---------- test/readString.spec.ts | 26 +++++++++++++++----------- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index a9eb1eb..e60996a 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-node-resolve": "^13.0.4", - "@types/jest": "^27.0.1", + "@types/jest": "^27.0.2", "@types/react": "^17.0.19", "@types/react-dom": "^17.0.9", "@types/react-test-renderer": "^17.0.1", @@ -119,7 +119,7 @@ }, { "path": "./dist/*.ts", - "maxSize": "750 B" + "maxSize": "1000 B" } ] } diff --git a/src/readRemoteFile.ts b/src/readRemoteFile.ts index 61348e5..f92a646 100644 --- a/src/readRemoteFile.ts +++ b/src/readRemoteFile.ts @@ -1,9 +1,5 @@ -import PapaParse, { NODE_STREAM_INPUT } from 'papaparse'; -import { CustomConfig } from './model'; +import PapaParse, { ParseRemoteConfig } from 'papaparse'; -export function readRemoteFile( - url: typeof NODE_STREAM_INPUT, - options: CustomConfig = {}, -) { - PapaParse.parse(url, Object.assign({}, { download: true }, options)); +export function readRemoteFile(url: string, config: ParseRemoteConfig) { + PapaParse.parse(url, Object.assign({}, { download: true }, config)); } diff --git a/src/readString.ts b/src/readString.ts index 7ce4695..ee021c9 100644 --- a/src/readString.ts +++ b/src/readString.ts @@ -1,9 +1,8 @@ -import PapaParse, { NODE_STREAM_INPUT } from 'papaparse'; -import { CustomConfig } from './model'; +import PapaParse, { ParseWorkerConfig } from 'papaparse'; export function readString( - str: typeof NODE_STREAM_INPUT, - options: CustomConfig = {}, + csvString: string, + config: ParseWorkerConfig & { download?: false | undefined }, ) { - return PapaParse.parse(str, options); + return PapaParse.parse(csvString, config); } diff --git a/test/jsonToCSV.spec.ts b/test/jsonToCSV.spec.ts index be0a395..feac3fd 100644 --- a/test/jsonToCSV.spec.ts +++ b/test/jsonToCSV.spec.ts @@ -1,10 +1,10 @@ -import expect from 'expect' -import { jsonToCSV } from '../src/jsonToCSV' +import expect from 'expect'; +import { jsonToCSV } from '../src/jsonToCSV'; // eslint-disable-next-line no-undef describe('jsonToCSV', () => { // eslint-disable-next-line no-undef - it('should return a csv format as expected', function() { + it('should return a csv format as expected', function () { const fixtures = `[ { "Column 1": "1-1", @@ -30,14 +30,16 @@ describe('jsonToCSV', () => { "Column 3": 6, "Column 4": 7 } - ]` + ]`; const expected = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 2-1,2-2,2-3,2-4 3-1,3-2,3-3,3-4 -4,5,6,7` - const actual = jsonToCSV(fixtures) - expect(typeof actual).toBe('string') - expect(actual.split('\r\n').join('')).toEqual(expected.split('\n').join('')) - }) -}) +4,5,6,7`; + const actual = jsonToCSV(fixtures); + expect(typeof actual).toBe('string'); + expect(actual.split('\r\n').join('')).toEqual( + expected.split('\n').join('') + ); + }); +}); diff --git a/test/readString.spec.ts b/test/readString.spec.ts index 177ba79..122fb01 100644 --- a/test/readString.spec.ts +++ b/test/readString.spec.ts @@ -1,24 +1,28 @@ -import expect from 'expect' -import { readString } from '../src/readString' +import expect from 'expect'; +import { readString } from '../src/readString'; // eslint-disable-next-line no-undef describe('readString', () => { // eslint-disable-next-line no-undef - it('should return an array as expected', function() { + it('should return an array as expected', function () { const fixtures = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 2-1,2-2,2-3,2-4 3-1,3-2,3-3,3-4 -4,5,6,7` +4,5,6,7`; const expected = [ ['Column 1', 'Column 2', 'Column 3', 'Column 4'], ['1-1', '1-2', '1-3', '1-4'], ['2-1', '2-2', '2-3', '2-4'], ['3-1', '3-2', '3-3', '3-4'], - ['4', '5', '6', '7'] - ] - const actual = readString(fixtures) - expect(Array.isArray(actual.data)).toBe(true) - expect(actual.data).toEqual(expected) - }) -}) + ['4', '5', '6', '7'], + ]; + readString(fixtures, { + worker: true, + complete: (results) => { + expect(Array.isArray(results.data)).toBe(true); + expect(results.data).toEqual(expected); + }, + }); + }); +}); From 15767059add3c9b246fcfc173f69bbdca4bc972e Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 5 Nov 2021 11:44:58 +0700 Subject: [PATCH 019/140] Update docs --- README.md | 17 +++++++++++++---- demo/ReadString.js | 15 +++++++++------ supports/create-next-app/pages/index.tsx | 20 +++++++++++++++++++- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3856fc3..6dd11c6 100644 --- a/README.md +++ b/README.md @@ -404,13 +404,18 @@ export default class CSVDownloader extends Component { ```javascript import { readString } from 'react-papaparse' -const str = `Column 1,Column 2,Column 3,Column 4 +const csvString = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 2-1,2-2,2-3,2-4 3-1,3-2,3-3,3-4 4,5,6,7` -const results = readString(str) +readString(csvString, { + worker: true, + complete: (results) => { + console.log(results) + }, +}) ``` ### 🎀 readRemoteFile @@ -468,8 +473,12 @@ const results = jsonToCSV(jsonData) If you tell react-papaparse there is a header row, each row will be organized by field name instead of index. ```javascript -const results = readString(csvString { - header: true +readString(csvString, { + header: true, + worker: true, + complete: (results) => { + console.log(results); + }, }) ``` diff --git a/demo/ReadString.js b/demo/ReadString.js index 4111f34..5583e56 100644 --- a/demo/ReadString.js +++ b/demo/ReadString.js @@ -3,17 +3,20 @@ import { readString } from 'react-papaparse'; export default class ReadString extends Component { handleClick = () => { - const str = `Column 1,Column 2,Column 3,Column 4 + const csvString = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 2-1,2-2,2-3,2-4 3-1,3-2,3-3,3-4 4,5,6,7`; - const results = readString(str); - - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); + readString(csvString, { + worker: true, + complete: (results) => { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }, + }); }; render() { diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 55c9a58..77fc040 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import { CSVReader, CSVDownloader } from 'react-papaparse' +import { CSVReader, CSVDownloader, readString } from 'react-papaparse' export default function Home() { const [isReset, setIsReset] = useState(false); @@ -29,6 +29,23 @@ export default function Home() { console.log('---------------------------') } + const handleClick = () => { + const csvString = `Column 1,Column 2,Column 3,Column 4 +1-1,1-2,1-3,1-4 +2-1,2-2,2-3,2-4 +3-1,3-2,3-3,3-4 +4,5,6,7`; + + readString(csvString, { + worker: true, + complete: (results) => { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }, + }) + }; + return ( <> Download + ) } From e9fc2986ce6697ac173342375f73fc519c7c29cf Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 5 Nov 2021 12:02:00 +0700 Subject: [PATCH 020/140] Update format --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6dd11c6..2665d19 100644 --- a/README.md +++ b/README.md @@ -477,7 +477,7 @@ readString(csvString, { header: true, worker: true, complete: (results) => { - console.log(results); + console.log(results) }, }) ``` From 175392f6a68fefd6a03a215be01fb84e2fe42845 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 5 Nov 2021 12:02:39 +0700 Subject: [PATCH 021/140] Update format --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2665d19..ae171d7 100644 --- a/README.md +++ b/README.md @@ -414,7 +414,7 @@ readString(csvString, { worker: true, complete: (results) => { console.log(results) - }, + } }) ``` @@ -478,7 +478,7 @@ readString(csvString, { worker: true, complete: (results) => { console.log(results) - }, + } }) ``` From 657493d3d703866655a97e24a163c1c63dcacce8 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 5 Nov 2021 15:55:06 +0700 Subject: [PATCH 022/140] Update docs --- docs/package.json | 2 +- docs/src/components/screens/Demo.js | 18 +++++++++++------- .../components/screens/indexes/CSVParsing.js | 9 ++++++++- docs/src/components/screens/indexes/Comment.js | 6 +++++- .../components/screens/indexes/Delimiter.js | 9 ++++++--- docs/src/components/screens/indexes/Header.js | 6 +++++- .../screens/indexes/TypeConversion.js | 6 +++++- docs/src/components/screens/indexes/Welcome.js | 7 ++++++- 8 files changed, 47 insertions(+), 16 deletions(-) diff --git a/docs/package.json b/docs/package.json index 8ca2ab0..e275ef0 100644 --- a/docs/package.json +++ b/docs/package.json @@ -18,7 +18,7 @@ "raw-loader": "^4.0.0", "react": "^16.12.0", "react-dom": "^16.12.0", - "react-papaparse": "^3.17.0", + "react-papaparse": "^3.18.0", "react-tabs": "^3.1.0" }, "devDependencies": { diff --git a/docs/src/components/screens/Demo.js b/docs/src/components/screens/Demo.js index 4851800..f6ea303 100644 --- a/docs/src/components/screens/Demo.js +++ b/docs/src/components/screens/Demo.js @@ -53,13 +53,17 @@ export default class Demo extends Component { handleImportOffer = () => { const index = this.state.tabIndex; if (index === 0) { - const results = readString(this.state.str); - console.log('---------------------------'); - console.log('Parse complete!'); - console.log('Row count: ', results.data.length); - console.log('Errors: ', results.errors.length); - console.log('Results: ', results); - console.log('---------------------------'); + readString(this.state.str, { + worker: true, + complete: (results) => { + console.log('---------------------------'); + console.log('Parse complete!'); + console.log('Row count: ', results.data.length); + console.log('Errors: ', results.errors.length); + console.log('Results: ', results); + console.log('---------------------------'); + } + }); } else if (index === 1) { const results = this.state.csvData; if (results) { diff --git a/docs/src/components/screens/indexes/CSVParsing.js b/docs/src/components/screens/indexes/CSVParsing.js index 41b5571..d7b0664 100644 --- a/docs/src/components/screens/indexes/CSVParsing.js +++ b/docs/src/components/screens/indexes/CSVParsing.js @@ -17,7 +17,14 @@ const CSVParsing = () => { {`import { readString } from 'react-papaparse' -const results = readString(csvString, config) +const config = { + worker: true, + complete: (results) => { + console.log(results) + } +} + +readString(csvString, config) /* results = { diff --git a/docs/src/components/screens/indexes/Comment.js b/docs/src/components/screens/indexes/Comment.js index 33ee17c..ed81f83 100644 --- a/docs/src/components/screens/indexes/Comment.js +++ b/docs/src/components/screens/indexes/Comment.js @@ -15,7 +15,11 @@ const Comment = () => { {`// Mostly found in academia, some CSV files // may have commented lines in them -const results = readString(csvString { +readString(csvString, { + worker: true, + complete: (results) => { + console.log(results) + }, comments: '#' })`} diff --git a/docs/src/components/screens/indexes/Delimiter.js b/docs/src/components/screens/indexes/Delimiter.js index fcf743f..b0bdf94 100644 --- a/docs/src/components/screens/indexes/Delimiter.js +++ b/docs/src/components/screens/indexes/Delimiter.js @@ -15,9 +15,12 @@ const Delimiter = () => { {`import { readString } from 'react-papaparse' -const results = readString(csvString) - -console.log(results.meta.delimiter) +readString(csvString, { + worker: true, + complete: (results) => { + console.log(results.meta.delimiter) + } +}) `} diff --git a/docs/src/components/screens/indexes/Header.js b/docs/src/components/screens/indexes/Header.js index 9fd2e32..1e4c57f 100644 --- a/docs/src/components/screens/indexes/Header.js +++ b/docs/src/components/screens/indexes/Header.js @@ -14,7 +14,11 @@ const Header = () => {
             
               {`// Key data by field name instead of index/position
-const results = readString(csvString {
+readString(csvString, {
+  worker: true,
+  complete: (results) => {
+    console.log(results)
+  },
   header: true
 })`}
             
diff --git a/docs/src/components/screens/indexes/TypeConversion.js b/docs/src/components/screens/indexes/TypeConversion.js
index 42a3428..512db77 100644
--- a/docs/src/components/screens/indexes/TypeConversion.js
+++ b/docs/src/components/screens/indexes/TypeConversion.js
@@ -15,7 +15,11 @@ const TypeConversion = () => {
           
             
               {`// Converts numeric/boolean data
-const results = readString(csvString {
+readString(csvString, {
+  worker: true,
+  complete: (results) => {
+    console.log(results)
+  },
   dynamicTyping: true
 })`}
             
diff --git a/docs/src/components/screens/indexes/Welcome.js b/docs/src/components/screens/indexes/Welcome.js
index 1dce0d6..47e703a 100644
--- a/docs/src/components/screens/indexes/Welcome.js
+++ b/docs/src/components/screens/indexes/Welcome.js
@@ -33,7 +33,12 @@ const Welcome = () => {
               
                 
                   {`// Parse CSV string
-const data = readString(csvString)
+readString(csvString, {
+  worker: true,
+  complete: (results) => {
+    console.log(results)
+  }
+})
 
 // Convert back to CSV
 const csv = jsonToCSV(jsonData)

From e14ca4f1780c91d25e59a7e33681581c4cb8834c Mon Sep 17 00:00:00 2001
From: Bunlong 
Date: Fri, 5 Nov 2021 22:19:37 +0700
Subject: [PATCH 023/140] Update CHANGELOG

---
 CHANGELOG.md | 3 ++-
 README.md    | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6e8d9a..7459516 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,8 @@
 
 ### ✨ Bugs
 
-* Wrong Typescript CSVDownloader config type
+  * Upgrade dependency
+  * Change readString API
 
 Credits
 
diff --git a/README.md b/README.md
index ae171d7..a4f8c76 100644
--- a/README.md
+++ b/README.md
@@ -501,7 +501,8 @@ readRemoteFile('http://example.com/big.csv', {
 
 Latest version 3.18.0 (2021-11-05):
 
-  * Wrong Typescript CSVDownloader config type
+  * Upgrade dependency
+  * Change readString API
 
 Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/react-papaparse/blob/master/CHANGELOG.md).
 

From eb140858fa1dd0abcaa6dde05f79ccfe222ffce6 Mon Sep 17 00:00:00 2001
From: Bunlong 
Date: Sat, 6 Nov 2021 00:16:38 +0700
Subject: [PATCH 024/140] Update README

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index a4f8c76..1928505 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ FAQ:
 
 * [CSVReader](https://react-papaparse.github.io/docs#local-files) – React component that handles csv files input and returns its content as array.
 * [CSVDownloader](https://github.com/bunlong/react-papaparse#-csvdownloader) – React component that render the link/button which is clicked to download the data provided in CSV format.
-* [readString](https://react-papaparse.github.io/docs#strings) – The function that read CSV comma separated string and returns its content as array.
+* [readString](https://github.com/bunlong/react-papaparse#-readstring) – The function that read CSV comma separated string and returns its content as array.
 * [readRemoteFile](https://react-papaparse.github.io/docs#remote-files) – The function that read remote CSV files and returns its content as array.
 * [jsonToCSV](https://react-papaparse.github.io/docs#json-to-csv) – The function that read an array of object (json) and returns its content as CSV comma separated string.
 

From 192283cdf838273b6d8804ef5c66a32fe2219f5c Mon Sep 17 00:00:00 2001
From: Bunlong 
Date: Sat, 6 Nov 2021 11:24:25 +0700
Subject: [PATCH 025/140] Update README

---
 README.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 1928505..5a9be68 100644
--- a/README.md
+++ b/README.md
@@ -51,11 +51,11 @@ FAQ:
 
 ## 📚 Useful Features
 
-* [CSVReader](https://react-papaparse.github.io/docs#local-files) – React component that handles csv files input and returns its content as array.
-* [CSVDownloader](https://github.com/bunlong/react-papaparse#-csvdownloader) – React component that render the link/button which is clicked to download the data provided in CSV format.
+* [CSVReader](https://github.com/Bunlong/react-papaparse#-csvreader) – React component that handles csv files input and returns its content as array.
+* [CSVDownloader](https://github.com/Bunlong/react-papaparse#-csvdownloader) – React component that render the link/button which is clicked to download the data provided in CSV format.
 * [readString](https://github.com/bunlong/react-papaparse#-readstring) – The function that read CSV comma separated string and returns its content as array.
-* [readRemoteFile](https://react-papaparse.github.io/docs#remote-files) – The function that read remote CSV files and returns its content as array.
-* [jsonToCSV](https://react-papaparse.github.io/docs#json-to-csv) – The function that read an array of object (json) and returns its content as CSV comma separated string.
+* [readRemoteFile](https://github.com/Bunlong/react-papaparse#-readremotefile) – The function that read remote CSV files and returns its content as array.
+* [jsonToCSV](https://github.com/Bunlong/react-papaparse#-jsontocsv) – The function that read an array of object (json) and returns its content as CSV comma separated string.
 
 ## 💡 Usage
 

From f2a5b1f3f66ca7a91e3d6e0cceb34fc9184f109a Mon Sep 17 00:00:00 2001
From: Bunlong 
Date: Sat, 6 Nov 2021 15:52:54 +0700
Subject: [PATCH 026/140] Add CODE_OF_CONDUCT

---
 CODE_OF_CONDUCT.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)
 create mode 100644 CODE_OF_CONDUCT.md

diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..7febab6
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,49 @@
+# Contributor Code of Conduct
+
+Our open source projects are no exception. Trust, respect, collaboration and transparency are core values we believe should live and breathe within our projects. Our community welcomes participants from around the world with different experiences, unique perspectives, and great ideas to share.
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Attempting collaboration before conflict
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* Violence, threats of violence, or inciting others to commit self-harm
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, intentionally spreading misinformation, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Abuse of the reporting process to intentionally harass or exclude others
+* Advocating for, or encouraging, any of the above behavior
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at `bunlong.van@gmail.com`. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/)

From d691649ad88f0bccd5b6da7604bf0233150fcb0f Mon Sep 17 00:00:00 2001
From: Bunlong 
Date: Sun, 7 Nov 2021 18:27:15 +0700
Subject: [PATCH 027/140] Update CHANGELOG and upgrade version

---
 CHANGELOG.md | 10 ++++++++++
 README.md    | 14 +++++++++++---
 package.json |  2 +-
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7459516..cad2863 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 3.18.1 (2021-11-07)
+
+### ✨ Bugs
+
+  * Fix issue: when component is unmounted immediately after file load in CSVReader
+
+Credits
+
+* Joey Baker [@joey-b](https://github.com/joey-b)
+
 ## 3.18.0 (2021-11-05)
 
 ### ✨ Bugs
diff --git a/README.md b/README.md
index 5a9be68..33fd592 100644
--- a/README.md
+++ b/README.md
@@ -499,10 +499,9 @@ readRemoteFile('http://example.com/big.csv', {
 
 ## 📜 Changelog
 
-Latest version 3.18.0 (2021-11-05):
+Latest version 3.18.1 (2021-11-07):
 
-  * Upgrade dependency
-  * Change readString API
+  * Fix issue: when component is unmounted immediately after file load in CSVReader
 
 Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/react-papaparse/blob/master/CHANGELOG.md).
 
@@ -768,6 +767,15 @@ How to contribute:
         
       
     
+    
+      
+        Joey Baker
+        
+ + Joey Baker + +
+ diff --git a/package.json b/package.json index e60996a..1820be0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-papaparse", - "version": "3.18.0", + "version": "3.18.1", "description": "The fastest in-browser CSV (or delimited text) parser for React. It is full of useful features such as CSVReader, CSVDownloader, readString, jsonToCSV, readRemoteFile, ... etc.", "author": "Bunlong ", "license": "MIT", From abd77311b0177bc5612cfcd1992ffbcfb10dd446 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 7 Nov 2021 22:58:29 +0700 Subject: [PATCH 028/140] Update CHANGELOG --- CHANGELOG.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cad2863..a320d55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### ✨ Bugs - * Fix issue: when component is unmounted immediately after file load in CSVReader + * Fix a bug when component is unmounted immediately after file load in CSVReader Credits diff --git a/README.md b/README.md index 33fd592..94d0d73 100644 --- a/README.md +++ b/README.md @@ -501,7 +501,7 @@ readRemoteFile('http://example.com/big.csv', { Latest version 3.18.1 (2021-11-07): - * Fix issue: when component is unmounted immediately after file load in CSVReader + * Fix a bug when component is unmounted immediately after file load in CSVReader Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/react-papaparse/blob/master/CHANGELOG.md). From 3c5ea73d7a6becc904428523ba758e658ba51590 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 8 Nov 2021 10:14:03 +0700 Subject: [PATCH 029/140] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94d0d73..ec4612c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ FAQ: * [CSVReader](https://github.com/Bunlong/react-papaparse#-csvreader) – React component that handles csv files input and returns its content as array. * [CSVDownloader](https://github.com/Bunlong/react-papaparse#-csvdownloader) – React component that render the link/button which is clicked to download the data provided in CSV format. -* [readString](https://github.com/bunlong/react-papaparse#-readstring) – The function that read CSV comma separated string and returns its content as array. +* [readString](https://github.com/Bunlong/react-papaparse#-readstring) – The function that read CSV comma separated string and returns its content as array. * [readRemoteFile](https://github.com/Bunlong/react-papaparse#-readremotefile) – The function that read remote CSV files and returns its content as array. * [jsonToCSV](https://github.com/Bunlong/react-papaparse#-jsontocsv) – The function that read an array of object (json) and returns its content as CSV comma separated string. From a302eee5baafa3a3333d7a4bc88e5d8ba75a7fcc Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 18 Nov 2021 23:44:21 +0700 Subject: [PATCH 030/140] Create useCSVDownloader --- src/useCSVDownloader.tsx | 18 ++++++++++++++++++ supports/create-next-app/pages/index.tsx | 9 ++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/useCSVDownloader.tsx diff --git a/src/useCSVDownloader.tsx b/src/useCSVDownloader.tsx new file mode 100644 index 0000000..48d3368 --- /dev/null +++ b/src/useCSVDownloader.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import PapaParse, { UnparseConfig } from 'papaparse'; + +export interface Props { + +} + +export interface Api { + +} + +function useCSVDownloaderComponent(api: Api) { + +} + +export function useCSVDownloader() { + +} diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 77fc040..979c673 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,9 +1,16 @@ import React, { useState } from 'react' -import { CSVReader, CSVDownloader, readString } from 'react-papaparse' +import { + CSVReader, + CSVDownloader, + // readString, + usePapaParse, +} from 'react-papaparse' export default function Home() { const [isReset, setIsReset] = useState(false); + const { readString } = usePapaParse(); + const handleReset = () => { setIsReset(!isReset) } From 05cfb3b87acf2986caf7608aa05d12773c9e5a62 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 19 Nov 2021 00:16:46 +0700 Subject: [PATCH 031/140] Create button and link download in useCSVDownloader --- src/useCSVDownloader.tsx | 149 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 2 deletions(-) diff --git a/src/useCSVDownloader.tsx b/src/useCSVDownloader.tsx index 48d3368..64c1176 100644 --- a/src/useCSVDownloader.tsx +++ b/src/useCSVDownloader.tsx @@ -1,18 +1,163 @@ import React from 'react'; import PapaParse, { UnparseConfig } from 'papaparse'; -export interface Props { +const Type = { + Link: 'link', + Button: 'button', +} as const; +export interface Props { + children: React.ReactNode; + data: any; + filename: string; + type?: 'link' | 'button'; + style?: any; + className?: string; + bom?: boolean; + config?: UnparseConfig; } export interface Api { - + data: any; + setData?: () => void; + filename: string; + setFilename?: () => void; + type: string; + setType: () => void; + style?: any; + setStyle?: () => void; + className?: any; + setClassName?: () => void; + bom?: boolean; + setBom?: () => void; + config?: UnparseConfig; + setConfig?: () => void; } function useCSVDownloaderComponent(api: Api) { + const CSVDownloaderComponent = (props: Props) => { + const { + setData, + data, + setFilename, + filename, + setType, + type, + setStyle, + style, + className, + setClassName, + bom, + setBom, + config, + setConfig, + } = CSVDownloader.api; + + React.useEffect(() => { + const { data, filename, type, style, className, bom, config } = props; + setData(data); + setFilename(filename); + type && setType(type); + style && setStyle(style); + className && setClassName(className); + bom && setBom(bom); + config && setConfig(config); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const download = () => { + const bomCode = bom ? '\ufeff' : ''; + let csvContent = null; + let csvURL = null; + + if (typeof data === 'function') { + setData(data()); + } + + if (typeof data === 'object') { + csvContent = PapaParse.unparse(data, config); + } else { + csvContent = data; + } + + const csvData = new Blob([`${bomCode}${csvContent}`], { + type: 'text/csv;charset=utf-8;', + }); + const navObj: any = window.navigator; + if (navObj.msSaveBlob) { + csvURL = navObj.msSaveBlob(csvData, `${filename}.csv`); + } else { + csvURL = window.URL.createObjectURL(csvData); + } + + const link = document.createElement('a'); + link.href = csvURL as string; + link.setAttribute('download', `${filename}.csv`); + link.click(); + link.remove(); + }; + + return ( + <> + {type === Type.Button ? ( + + ) : ( + download()} style={style} className={className}> + {props.children} + + )} + + ); + }; + + const CSVDownloader = React.useMemo( + () => CSVDownloaderComponent, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ) as any; + + CSVDownloader.api = api; + + return CSVDownloader; } export function useCSVDownloader() { + const [data, setData] = React.useState({}); + const [filename, setFilename] = React.useState({}); + const [type, setType] = React.useState(Type.Link); + const [style, setStyle] = React.useState({}); + const [className, setClassName] = React.useState(''); + const [bom, setBom] = React.useState(false); + const [config, setConfig] = React.useState({}); + const api = { + data, + setData, + filename, + setFilename, + type, + setType, + style, + setStyle, + className, + setClassName, + bom, + setBom, + config, + setConfig, + } as Api; + + const ExcelDownloder = useCSVDownloaderComponent(api); + return { + ...api, + ExcelDownloder, + Type, + }; } From cde6e6a551088df33bbaf7af8eda2ec434ab2203 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 19 Nov 2021 00:19:28 +0700 Subject: [PATCH 032/140] Create button and link download in useCSVDownloader --- src/useCSVDownloader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/useCSVDownloader.tsx b/src/useCSVDownloader.tsx index 64c1176..2f6197f 100644 --- a/src/useCSVDownloader.tsx +++ b/src/useCSVDownloader.tsx @@ -153,11 +153,11 @@ export function useCSVDownloader() { setConfig, } as Api; - const ExcelDownloder = useCSVDownloaderComponent(api); + const CSVDownloader = useCSVDownloaderComponent(api); return { ...api, - ExcelDownloder, + CSVDownloader, Type, }; } From feacdd8471ff6748cede4ff4fc245c3481a95f53 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 19 Nov 2021 15:51:18 +0700 Subject: [PATCH 033/140] Import useCSVDownloader --- src/react-papaparse.ts | 2 + supports/create-next-app/pages/index.tsx | 218 ++++++++++++----------- 2 files changed, 114 insertions(+), 106 deletions(-) diff --git a/src/react-papaparse.ts b/src/react-papaparse.ts index eee01ae..86fb124 100644 --- a/src/react-papaparse.ts +++ b/src/react-papaparse.ts @@ -17,4 +17,6 @@ export { export { readString } from './readString'; export { readRemoteFile } from './readRemoteFile'; export { jsonToCSV } from './jsonToCSV'; + export { usePapaParse } from './usePapaParse'; +export { useCSVDownloader } from './useCSVDownloader'; diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 979c673..2817c75 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,13 +1,15 @@ import React, { useState } from 'react' import { CSVReader, - CSVDownloader, + // CSVDownloader, // readString, usePapaParse, + useCSVDownloader, } from 'react-papaparse' export default function Home() { const [isReset, setIsReset] = useState(false); + const { CSVDownloader, Type } = useCSVDownloader(); const { readString } = usePapaParse(); @@ -55,117 +57,121 @@ export default function Home() { return ( <> - - Click to upload. - - - - Download - - - Download - - { - return [ +
+ + Click to upload. + +
+
+ + + - Download - - + filename={'filename'} + config={ + { + delimiter: ';', + } + } + type={Type.Button} + > + Download + + + Download + + { + return [ + { + "Column 1": "1-1", + "Column 2": "1-2", + "Column 3": "1-3", + "Column 4": "1-4", + } + ]} + } + > + Download + +
) } From 91c09ec26762189e7ad9879720892c2e9be9c5a5 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 19 Nov 2021 17:29:16 +0700 Subject: [PATCH 034/140] Add useCSVReader.tsx --- src/useCSVReader.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/useCSVReader.tsx diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx new file mode 100644 index 0000000..e69de29 From f700e410a51ce636137f45fefbb9d721be7f3900 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 19 Nov 2021 17:46:58 +0700 Subject: [PATCH 035/140] Define initialize classes --- src/useCSVReader.tsx | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index e69de29..79d5da3 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +export interface Props { +} + +export interface Api { +} + +function useCSVReaderComponent(api: Api) { + const CSVReaderComponent = (props: Props) => { + return ( + <> + Hello + + ); + } + + const CSVReader = React.useMemo( + () => CSVReaderComponent, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ) as any; + + CSVReader.api = api; + + return CSVReader; +} + +export function useCSVReader() { + +} From 164761ed7dc2ffceb64b8101866d0bb152c97f7d Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sat, 20 Nov 2021 00:03:09 +0700 Subject: [PATCH 036/140] Define Props and Interface --- src/react-papaparse.ts | 1 + src/useCSVReader.tsx | 116 ++++++++++++++++++++--- supports/create-next-app/pages/index.tsx | 13 ++- 3 files changed, 116 insertions(+), 14 deletions(-) diff --git a/src/react-papaparse.ts b/src/react-papaparse.ts index 86fb124..7460c35 100644 --- a/src/react-papaparse.ts +++ b/src/react-papaparse.ts @@ -20,3 +20,4 @@ export { jsonToCSV } from './jsonToCSV'; export { usePapaParse } from './usePapaParse'; export { useCSVDownloader } from './useCSVDownloader'; +export { useCSVReader } from './useCSVReader'; diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 79d5da3..f7444b4 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -1,19 +1,71 @@ import React from 'react'; +import { /*PapaParse,*/ ParseResult } from 'papaparse'; +import { CustomConfig } from './model'; -export interface Props { +const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; + +export interface Props { + children: React.ReactNode; + config?: CustomConfig; + accept?: string; + className?: string; + style?: any; + progressBarColor?: string; + removeButtonColor?: string; + onDrop?: (data: Array>, file?: any) => void; + onFileLoad?: (data: Array>, file?: any) => void; + onError?: (err: any, file: any, inputElem: any, reason: any) => void; + onRemoveFile?: (data: null) => void; + noClick?: boolean; + noDrag?: boolean; + noRemoveButton?: boolean; + noProgressBar?: boolean; + isReset?: boolean; } -export interface Api { +// interface State { +// dropAreaCustom: any; +// progressBar: number; +// displayProgressBarStatus: string; +// file: any; +// timeout: any; +// files: any; +// removeIconColor: string; +// isCanceled: boolean; +// } + +export interface Api { + config?: CustomConfig; + setConfig?: () => void; + accept?: string; + setAccept?: () => void; + className?: string; + setClassName?: () => void; + style?: any; + setStyle?: () => void; + progressBarColor?: string; + setProgressBarColor?: () => void; + removeButtonColor?: string; + setRemoveButtonColor?: () => void; + + noClick?: boolean; + setNoClick?: () => void; + noDrag?: boolean; + setNoDrag?: () => void; + noRemoveButton?: boolean; + setNoRemoveButton?: () => void; + noProgressBar?: boolean; + setNoProgressBar?: () => void; + isReset?: boolean; + setIsReset?: () => void; } -function useCSVReaderComponent(api: Api) { - const CSVReaderComponent = (props: Props) => { - return ( - <> - Hello - - ); - } +function useCSVReaderComponent(api: Api) { + const CSVReaderComponent = (props: Props) => { + console.log(props); + + return <>Hello; + }; const CSVReader = React.useMemo( () => CSVReaderComponent, @@ -26,6 +78,48 @@ function useCSVReaderComponent(api: Api) { return CSVReader; } -export function useCSVReader() { +export function useCSVReader() { + const [config, setConfig] = React.useState({}); + const [accept, setAccept] = React.useState(DEFAULT_ACCEPT); + const [className, setClassName] = React.useState(''); + const [style, setStyle] = React.useState({}); + const [progressBarColor, setProgressBarColor] = React.useState(''); + const [removeButtonColor, setRemoveButtonColor] = React.useState(''); + const [noClick, setNoClick] = React.useState(false); + const [noDrag, setNoDrag] = React.useState(false); + const [noRemoveButton, setNoRemoveButton] = React.useState(false); + const [noProgressBar, setNoProgressBar] = React.useState(false); + const [isReset, setIsReset] = React.useState(false); + + const api = { + config, + setConfig, + accept, + setAccept, + className, + setClassName, + style, + setStyle, + progressBarColor, + setProgressBarColor, + removeButtonColor, + setRemoveButtonColor, + noClick, + setNoClick, + noDrag, + setNoDrag, + noRemoveButton, + setNoRemoveButton, + noProgressBar, + setNoProgressBar, + isReset, + setIsReset, + } as Api; + + const CSVReader = useCSVReaderComponent(api); + return { + ...api, + CSVReader, + }; } diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 2817c75..5967257 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,15 +1,17 @@ import React, { useState } from 'react' import { - CSVReader, + // CSVReader, // CSVDownloader, // readString, usePapaParse, useCSVDownloader, + useCSVReader, } from 'react-papaparse' export default function Home() { const [isReset, setIsReset] = useState(false); const { CSVDownloader, Type } = useCSVDownloader(); + const { CSVReader } = useCSVReader(); const { readString } = usePapaParse(); @@ -58,7 +60,7 @@ export default function Home() { return ( <>
- Click to upload. - + */}
@@ -172,6 +174,11 @@ export default function Home() { Download
+
+ +
) } From 52f4b26f0f43f8a0af7372e549432201e40950fd Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 21 Nov 2021 00:42:45 +0700 Subject: [PATCH 037/140] Add onClick event --- src/useCSVReader.tsx | 136 ++++++++++++++++++++++- src/utils.ts | 46 ++++++++ supports/create-next-app/pages/index.tsx | 6 +- 3 files changed, 182 insertions(+), 6 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index f7444b4..65a911e 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -1,9 +1,16 @@ -import React from 'react'; +import React, { CSSProperties, useReducer, useCallback, useMemo } from 'react'; import { /*PapaParse,*/ ParseResult } from 'papaparse'; import { CustomConfig } from './model'; +import { isIeOrEdge, composeEventHandlers } from './utils'; const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; +const cssProperty = { + inputFile: { + display: 'none', + } as CSSProperties, +}; + export interface Props { children: React.ReactNode; config?: CustomConfig; @@ -16,6 +23,7 @@ export interface Props { onFileLoad?: (data: Array>, file?: any) => void; onError?: (err: any, file: any, inputElem: any, reason: any) => void; onRemoveFile?: (data: null) => void; + disabled?: boolean; noClick?: boolean; noDrag?: boolean; noRemoveButton?: boolean; @@ -58,13 +66,103 @@ export interface Api { setNoProgressBar?: () => void; isReset?: boolean; setIsReset?: () => void; + disabled?: boolean; + setDisabled?: () => void; } function useCSVReaderComponent(api: Api) { const CSVReaderComponent = (props: Props) => { - console.log(props); + const { + // config, + setConfig, + accept, + setAccept, + noClick, + // setNoClick, + disabled, + setDisabled, + } = CSVReader.api; + + const inputFileRef: any = React.useRef(null); + + const [state, dispatch] = useReducer(reducer, initialState); + + console.log(state); + + React.useEffect(() => { + const { config, accept } = props; + config && setConfig(config); + accept && setAccept(accept); + disabled && setDisabled(disabled); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const childrenIsFunction = () => { + return typeof props.children === 'function'; + }; + + // Fn for opening the file dialog programmatically + const openFileDialog = useCallback(() => { + // if (inputFileRef.current && state.displayProgressBarStatus) { + if (inputFileRef.current) { + dispatch({ type: 'openDialog' }); + inputFileRef.current.value = null; + inputFileRef.current.click(); + } + }, [dispatch]); - return <>Hello; + // Cb to open the file dialog when click occurs on the dropzone + const onClickCb = useCallback(() => { + 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(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inputFileRef, noClick]); + + const composeHandler = (fn: any) => { + return disabled ? null : fn; + }; + + const getProps = useMemo( + () => + ({ onClick = () => {}, ...rest } = {}) => ({ + onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), + ...rest, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [onClickCb], + ); + + return ( + <> + + {!childrenIsFunction() ? ( +
+ {props.children} +
+ ) : ( +
+ {props.children} +
+ )} + + ); }; const CSVReader = React.useMemo( @@ -90,6 +188,7 @@ export function useCSVReader() { const [noRemoveButton, setNoRemoveButton] = React.useState(false); const [noProgressBar, setNoProgressBar] = React.useState(false); const [isReset, setIsReset] = React.useState(false); + const [disabled, setDisabled] = React.useState(false); const api = { config, @@ -114,6 +213,8 @@ export function useCSVReader() { setNoProgressBar, isReset, setIsReset, + disabled, + setDisabled, } as Api; const CSVReader = useCSVReaderComponent(api); @@ -123,3 +224,32 @@ export function useCSVReader() { CSVReader, }; } + +const initialState = { + // isFocused: false, + // isDragActive: false, + // isDragAccept: false, + // isDragReject: false, + // draggedFiles: [], + // acceptedFiles: [], + // fileRejections: [], + displayProgressBarStatus: 'none', + isFileDialogActive: false, +}; + +function reducer(state: any, action: any) { + switch (action.type) { + case 'openDialog': + return { + ...state, + isFileDialogActive: true, + }; + case 'closeDialog': + return { + ...state, + isFileDialogActive: false, + }; + default: + return state; + } +} diff --git a/src/utils.ts b/src/utils.ts index 02dbf8f..d7419d4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -49,3 +49,49 @@ export function lightenDarkenColor(col: string, amt: number) { } return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16); } + +function isIe(userAgent: any) { + return ( + userAgent.indexOf('MSIE') !== -1 || userAgent.indexOf('Trident/') !== -1 + ); +} + +function isEdge(userAgent: any) { + return userAgent.indexOf('Edge/') !== -1; +} + +export function isIeOrEdge(userAgent = window.navigator.userAgent) { + return isIe(userAgent) || isEdge(userAgent); +} + +// 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: any) { + if (typeof event.isPropagationStopped === 'function') { + return event.isPropagationStopped(); + } else if (typeof event.cancelBubble !== 'undefined') { + return event.cancelBubble; + } + return false; +} + +/** + * 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: any[]) { + return (event: any, ...args: any[]) => + fns.some((fn) => { + if (!isPropagationStopped(event) && fn) { + fn(event, ...args); + } + return isPropagationStopped(event); + }); +} diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 5967257..e4a7224 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -175,9 +175,9 @@ export default function Home() {
- + + Read CSV +
) From 6708ed59b9cb4b50b55e2c4be1ec5d7ed216258d Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 22 Nov 2021 00:37:58 +0700 Subject: [PATCH 038/140] Setup input --- src/useCSVReader.tsx | 290 +++++++++++++++++++---- src/utils.ts | 66 ++++++ supports/create-next-app/pages/index.tsx | 9 +- 3 files changed, 319 insertions(+), 46 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 65a911e..f564da4 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -1,20 +1,37 @@ -import React, { CSSProperties, useReducer, useCallback, useMemo } from 'react'; +import React, { + // CSSProperties, + useReducer, + useCallback, + useMemo, + useEffect, + useState, + ReactNode, + useRef, +} from 'react'; import { /*PapaParse,*/ ParseResult } from 'papaparse'; import { CustomConfig } from './model'; -import { isIeOrEdge, composeEventHandlers } from './utils'; +import { + isIeOrEdge, + composeEventHandlers, + isEventWithFiles, + isPropagationStopped, + // fileAccepted, +} from './utils'; const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; -const cssProperty = { - inputFile: { - display: 'none', - } as CSSProperties, -}; +// const cssProperty = { +// inputFile: { +// display: 'none', +// } as CSSProperties, +// }; export interface Props { - children: React.ReactNode; + children: (fn: any) => void | ReactNode; config?: CustomConfig; accept?: string; + minSize?: number; + maxSize?: number; className?: string; style?: any; progressBarColor?: string; @@ -23,12 +40,15 @@ export interface Props { onFileLoad?: (data: Array>, file?: any) => void; onError?: (err: any, file: any, inputElem: any, reason: any) => void; onRemoveFile?: (data: null) => void; + onFileDialogCancel?: () => void; disabled?: boolean; noClick?: boolean; noDrag?: boolean; noRemoveButton?: boolean; noProgressBar?: boolean; isReset?: boolean; + noKeyboard?: boolean; + noDragEventsBubbling?: boolean; } // interface State { @@ -47,6 +67,10 @@ export interface Api { setConfig?: () => void; accept?: string; setAccept?: () => void; + minSize?: number; + setMinSize?: () => void; + maxSize?: number; + setMaxSize?: () => void; className?: string; setClassName?: () => void; style?: any; @@ -68,6 +92,10 @@ export interface Api { setIsReset?: () => void; disabled?: boolean; setDisabled?: () => void; + noKeyboard?: boolean; + setNoKeyboard?: () => void; + noDragEventsBubbling?: boolean; + setNoDragEventsBubbling?: () => void; } function useCSVReaderComponent(api: Api) { @@ -81,19 +109,23 @@ function useCSVReaderComponent(api: Api) { // setNoClick, disabled, setDisabled, + noDragEventsBubbling, + setNoDragEventsBubbling, } = CSVReader.api; - const inputFileRef: any = React.useRef(null); + const inputRef: any = useRef(null); + const rootRef: any = useRef(null); + const dragTargetsRef = useRef([]) const [state, dispatch] = useReducer(reducer, initialState); + const { isFileDialogActive } = state; - console.log(state); - - React.useEffect(() => { - const { config, accept } = props; + useEffect(() => { + const { config, accept, noDragEventsBubbling } = props; config && setConfig(config); accept && setAccept(accept); disabled && setDisabled(disabled); + noDragEventsBubbling && setNoDragEventsBubbling(noDragEventsBubbling) // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -103,11 +135,11 @@ function useCSVReaderComponent(api: Api) { // Fn for opening the file dialog programmatically const openFileDialog = useCallback(() => { - // if (inputFileRef.current && state.displayProgressBarStatus) { - if (inputFileRef.current) { + // if (inputRef.current && state.displayProgressBarStatus) { + if (inputRef.current) { dispatch({ type: 'openDialog' }); - inputFileRef.current.value = null; - inputFileRef.current.click(); + inputRef.current.value = null; + inputRef.current.click(); } }, [dispatch]); @@ -119,14 +151,165 @@ function useCSVReaderComponent(api: Api) { // 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(); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [inputFileRef, noClick]); + }, [inputRef, noClick]); + + // Update file dialog active state when the window is focused on + const onWindowFocus = () => { + // Execute the timeout only if the file dialog is opened in the browser + if (isFileDialogActive) { + setTimeout(() => { + if (inputRef.current) { + const { files } = inputRef.current; + + if (!files.length) { + dispatch({ type: 'closeDialog' }); + if (typeof props.onFileDialogCancel === 'function') { + props.onFileDialogCancel(); + } + } + } + }, 300); + } + }; + + useEffect(() => { + window.addEventListener('focus', onWindowFocus, false); + return () => { + window.removeEventListener('focus', onWindowFocus, false); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inputRef, isFileDialogActive, props.onFileDialogCancel]); + + const renderChildren = () => { + const { children } = props; + return childrenIsFunction() ? children(getProps) : children; + }; + + const onInputElementClick = useCallback(event => { + event.stopPropagation() + }, []); + + const stopPropagation = (event: any) => { + if (props.noDragEventsBubbling) { + event.stopPropagation() + } + }; + + const onDropCb = useCallback( + event => { + event.preventDefault() + // Persist here because we need the event later after getFilesFromEvent() is done + event.persist() + stopPropagation(event) + + dragTargetsRef.current = [] + + if (isEventWithFiles(event)) { + // Promise.resolve(getFilesFromEvent(event)).then(files => { + if (isPropagationStopped(event) && !noDragEventsBubbling) { + return + } + + + // const acceptedFiles = [] + // const fileRejections = [] + + // event.target.files.forEach((file: any) => { + // const [accepted, acceptError] = fileAccepted(file, accept); + // console.log('==========='); + // console.log(accepted) + // console.log(acceptError) + // console.log('==========='); + + // const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize) + // const customErrors = validator ? validator(file) : null; + + // if (accepted && sizeMatch && !customErrors) { + // acceptedFiles.push(file) + // } else { + // let errors = [acceptError, sizeError]; + + // if (customErrors) { + // errors = errors.concat(customErrors); + // } + + // fileRejections.push({ file, errors: errors.filter(e => e) }) + // } + // }) + + // if ((!multiple && acceptedFiles.length > 1) || (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles)) { + // // Reject everything and empty accepted files + // acceptedFiles.forEach(file => { + // fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }) + // }) + // acceptedFiles.splice(0) + // } + + // dispatch({ + // acceptedFiles, + // fileRejections, + // 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({ type: 'reset' }) + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [ + accept, + noDragEventsBubbling, + // multiple, + // minSize, + // maxSize, + // maxFiles, + // getFilesFromEvent, + // onDrop, + // onDropAccepted, + // onDropRejected, + // validator + ] + ); + + const getInputProps = useMemo( + () => ({ refKey = 'ref', onChange = () => {}, onClick = () => {}, ...rest } = {}) => { + const inputProps = { + accept, + // multiple, + type: 'file', + style: { display: 'none' }, + onChange: composeHandler(composeEventHandlers(onChange, onDropCb)), + onClick: composeHandler(composeEventHandlers(onClick, onInputElementClick)), + autoComplete: 'off', + tabIndex: -1, + [refKey]: inputRef + } + + return { + ...inputProps, + ...rest + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [inputRef, accept, onDropCb, disabled] + ); const composeHandler = (fn: any) => { return disabled ? null : fn; @@ -142,22 +325,27 @@ function useCSVReaderComponent(api: Api) { [onClickCb], ); + const getRootProps = useMemo( + () => + ({ onClick = () => {}, ...rest } = {}) => ({ + onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), + ...rest, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [onClickCb], + ); + return ( <> - {!childrenIsFunction() ? ( -
- {props.children} -
+ {childrenIsFunction() ? ( + // button + <>{renderChildren()} ) : ( -
+ // drop div +
{props.children}
)} @@ -165,7 +353,7 @@ function useCSVReaderComponent(api: Api) { ); }; - const CSVReader = React.useMemo( + const CSVReader = useMemo( () => CSVReaderComponent, // eslint-disable-next-line react-hooks/exhaustive-deps [], @@ -177,24 +365,32 @@ function useCSVReaderComponent(api: Api) { } export function useCSVReader() { - const [config, setConfig] = React.useState({}); - const [accept, setAccept] = React.useState(DEFAULT_ACCEPT); - const [className, setClassName] = React.useState(''); - const [style, setStyle] = React.useState({}); - const [progressBarColor, setProgressBarColor] = React.useState(''); - const [removeButtonColor, setRemoveButtonColor] = React.useState(''); - const [noClick, setNoClick] = React.useState(false); - const [noDrag, setNoDrag] = React.useState(false); - const [noRemoveButton, setNoRemoveButton] = React.useState(false); - const [noProgressBar, setNoProgressBar] = React.useState(false); - const [isReset, setIsReset] = React.useState(false); - const [disabled, setDisabled] = React.useState(false); + const [config, setConfig] = useState({}); + const [accept, setAccept] = useState(DEFAULT_ACCEPT); + const [minSize, setMinSize] = useState(0); + const [maxSize, setMaxSize] = useState(0); + const [className, setClassName] = useState(''); + const [style, setStyle] = useState({}); + const [progressBarColor, setProgressBarColor] = useState(''); + const [removeButtonColor, setRemoveButtonColor] = useState(''); + const [noClick, setNoClick] = useState(false); + const [noDrag, setNoDrag] = useState(false); + const [noRemoveButton, setNoRemoveButton] = useState(false); + const [noProgressBar, setNoProgressBar] = useState(false); + const [isReset, setIsReset] = useState(false); + const [disabled, setDisabled] = useState(false); + const [noKeyboard, setNoKeyboard] = useState(false); + const [noDragEventsBubbling, setNoDragEventsBubbling] = useState(false); const api = { config, setConfig, accept, setAccept, + minSize, + setMinSize, + maxSize, + setMaxSize, className, setClassName, style, @@ -215,6 +411,10 @@ export function useCSVReader() { setIsReset, disabled, setDisabled, + noKeyboard, + setNoKeyboard, + noDragEventsBubbling, + setNoDragEventsBubbling, } as Api; const CSVReader = useCSVReaderComponent(api); @@ -226,6 +426,8 @@ export function useCSVReader() { } const initialState = { + displayProgressBarStatus: 'none', + isFileDialogActive: false, // isFocused: false, // isDragActive: false, // isDragAccept: false, @@ -233,8 +435,6 @@ const initialState = { // draggedFiles: [], // acceptedFiles: [], // fileRejections: [], - displayProgressBarStatus: 'none', - isFileDialogActive: false, }; function reducer(state: any, action: any) { diff --git a/src/utils.ts b/src/utils.ts index d7419d4..024141d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,6 @@ +// Error codes +export const FILE_INVALID_TYPE = 'file-invalid-type' + export default function getSize(size: number) { const sizeKb = 1024; const sizeMb = sizeKb * sizeKb; @@ -95,3 +98,66 @@ export function composeEventHandlers(...fns: any[]) { return isPropagationStopped(event); }); } + +export function isEventWithFiles(event: any) { + 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' + ) +} + +/** + * Check if the provided file type should be accepted by the input with accept attribute. + * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input#attr-accept + * + * Inspired by https://github.com/enyo/dropzone + * + * @param file {File} https://developer.mozilla.org/en-US/docs/Web/API/File + * @param acceptedFiles {string} + * @returns {boolean} + */ + +export function accepts(file: any, acceptedFiles: any) { + if (file && acceptedFiles) { + const acceptedFilesArray = Array.isArray(acceptedFiles) + ? acceptedFiles + : acceptedFiles.split(',') + const fileName = file.name || '' + const mimeType = (file.type || '').toLowerCase() + const baseMimeType = mimeType.replace(/\/.*$/, '') + + return acceptedFilesArray.some((type: any) => { + const validType = type.trim().toLowerCase() + if (validType.charAt(0) === '.') { + return fileName.toLowerCase().endsWith(validType) + } else if (validType.endsWith('/*')) { + // This is something like a image/* mime type + return baseMimeType === validType.replace(/\/.*$/, '') + } + return mimeType === validType + }) + } + return true +} + +// File Errors +export const getInvalidTypeRejectionErr = (accept: any) => { + accept = Array.isArray(accept) && accept.length === 1 ? accept[0] : accept + const messageSuffix = Array.isArray(accept) ? `one of ${accept.join(', ')}` : accept + return { + code: FILE_INVALID_TYPE, + message: `File type must be ${messageSuffix}` + } +} + +// Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with +// that MIME type will always be accepted +export function fileAccepted(file: any, accept: any) { + const isAcceptable = file.type === 'application/x-moz-file' || accepts(file, accept) + return [isAcceptable, isAcceptable ? null : getInvalidTypeRejectionErr(accept)] +} diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index e4a7224..292b924 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -176,7 +176,14 @@ export default function Home() {
- Read CSV + {(getProps) => ( + + )}
From b44ddbc699617842038cdebc1b6047b2697bde37 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 22 Nov 2021 14:56:53 +0700 Subject: [PATCH 039/140] Validate files --- src/useCSVReader.tsx | 41 +++++++++++++++++++++++------------------ src/utils.ts | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index f564da4..76bff71 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -15,7 +15,8 @@ import { composeEventHandlers, isEventWithFiles, isPropagationStopped, - // fileAccepted, + fileAccepted, + fileMatchSize, } from './utils'; const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; @@ -111,6 +112,10 @@ function useCSVReaderComponent(api: Api) { setDisabled, noDragEventsBubbling, setNoDragEventsBubbling, + minSize, + setMinSize, + maxSize, + setMaxSize, } = CSVReader.api; const inputRef: any = useRef(null); @@ -121,11 +126,13 @@ function useCSVReaderComponent(api: Api) { const { isFileDialogActive } = state; useEffect(() => { - const { config, accept, noDragEventsBubbling } = props; + const { config, accept, noDragEventsBubbling, minSize, maxSize } = props; config && setConfig(config); accept && setAccept(accept); disabled && setDisabled(disabled); noDragEventsBubbling && setNoDragEventsBubbling(noDragEventsBubbling) + minSize && setMinSize(minSize); + maxSize && setMaxSize(maxSize); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -212,23 +219,21 @@ function useCSVReaderComponent(api: Api) { if (isEventWithFiles(event)) { // Promise.resolve(getFilesFromEvent(event)).then(files => { - if (isPropagationStopped(event) && !noDragEventsBubbling) { - return - } + if (isPropagationStopped(event) && !noDragEventsBubbling) { + return + } + // const acceptedFiles = [] + // const fileRejections = [] - // const acceptedFiles = [] - // const fileRejections = [] - - // event.target.files.forEach((file: any) => { - // const [accepted, acceptError] = fileAccepted(file, accept); - // console.log('==========='); - // console.log(accepted) - // console.log(acceptError) - // console.log('==========='); - - // const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize) - // const customErrors = validator ? validator(file) : null; + Array.from(event.target.files).forEach((file: any) => { + // const [accepted, acceptError] = fileAccepted(file, accept); + const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize) + console.log('======================'); + console.log(sizeMatch); + console.log(sizeError); + console.log('======================'); + // const customErrors = validator ? validator(file) : null; // if (accepted && sizeMatch && !customErrors) { // acceptedFiles.push(file) @@ -268,7 +273,7 @@ function useCSVReaderComponent(api: Api) { // if (acceptedFiles.length > 0 && onDropAccepted) { // onDropAccepted(acceptedFiles, event) // } - // }) + }) } // dispatch({ type: 'reset' }) }, diff --git a/src/utils.ts b/src/utils.ts index 024141d..cb48be8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,7 @@ // Error codes -export const FILE_INVALID_TYPE = 'file-invalid-type' +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 default function getSize(size: number) { const sizeKb = 1024; @@ -161,3 +163,34 @@ export function fileAccepted(file: any, accept: any) { const isAcceptable = file.type === 'application/x-moz-file' || accepts(file, accept) return [isAcceptable, isAcceptable ? null : getInvalidTypeRejectionErr(accept)] } + +export function fileMatchSize(file: any, minSize: any, maxSize: any) { + 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: any) { + return value !== undefined && value !== null +} + +export const getTooLargeRejectionErr = (maxSize: any) => { + return { + code: FILE_TOO_LARGE, + message: `File is larger than ${maxSize} bytes` + } +} + +export const getTooSmallRejectionErr = (minSize: any) => { + return { + code: FILE_TOO_SMALL, + message: `File is smaller than ${minSize} bytes` + } +} From c1114be3935e249bba54c54af3cc3000abf57c29 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Tue, 23 Nov 2021 00:02:22 +0700 Subject: [PATCH 040/140] Read accepted files --- src/useCSVReader.tsx | 126 +++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 59 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 76bff71..9756718 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -37,6 +37,7 @@ export interface Props { style?: any; progressBarColor?: string; removeButtonColor?: string; + validator?: (file: any) => void; onDrop?: (data: Array>, file?: any) => void; onFileLoad?: (data: Array>, file?: any) => void; onError?: (err: any, file: any, inputElem: any, reason: any) => void; @@ -80,6 +81,8 @@ export interface Api { setProgressBarColor?: () => void; removeButtonColor?: string; setRemoveButtonColor?: () => void; + validator?: null; + setValidator?: () => void; noClick?: boolean; setNoClick?: () => void; @@ -116,6 +119,8 @@ function useCSVReaderComponent(api: Api) { setMinSize, maxSize, setMaxSize, + validator, + setValidator, } = CSVReader.api; const inputRef: any = useRef(null); @@ -126,13 +131,14 @@ function useCSVReaderComponent(api: Api) { const { isFileDialogActive } = state; useEffect(() => { - const { config, accept, noDragEventsBubbling, minSize, maxSize } = props; + const { config, accept, noDragEventsBubbling, minSize, maxSize, validator } = props; config && setConfig(config); accept && setAccept(accept); disabled && setDisabled(disabled); noDragEventsBubbling && setNoDragEventsBubbling(noDragEventsBubbling) minSize && setMinSize(minSize); maxSize && setMaxSize(maxSize); + validator && setValidator(validator); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -218,78 +224,77 @@ function useCSVReaderComponent(api: Api) { dragTargetsRef.current = [] if (isEventWithFiles(event)) { - // Promise.resolve(getFilesFromEvent(event)).then(files => { if (isPropagationStopped(event) && !noDragEventsBubbling) { return } - // const acceptedFiles = [] - // const fileRejections = [] - - Array.from(event.target.files).forEach((file: any) => { - // const [accepted, acceptError] = fileAccepted(file, accept); + const acceptedFiles = [] as any; + const fileRejections = [] as any; + + Array.from(event.target.files).forEach(file => { + const [accepted, acceptError] = fileAccepted(file, accept) const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize) - console.log('======================'); - console.log(sizeMatch); - console.log(sizeError); - console.log('======================'); - // const customErrors = validator ? validator(file) : null; - - // if (accepted && sizeMatch && !customErrors) { - // acceptedFiles.push(file) - // } else { - // let errors = [acceptError, sizeError]; - - // if (customErrors) { - // errors = errors.concat(customErrors); - // } - - // fileRejections.push({ file, errors: errors.filter(e => e) }) - // } - // }) - - // if ((!multiple && acceptedFiles.length > 1) || (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles)) { - // // Reject everything and empty accepted files - // acceptedFiles.forEach(file => { - // fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }) - // }) - // acceptedFiles.splice(0) - // } - - // dispatch({ - // acceptedFiles, - // fileRejections, - // type: 'setFiles' - // }) - - // if (onDrop) { - // onDrop(acceptedFiles, fileRejections, event) - // } - - // if (fileRejections.length > 0 && onDropRejected) { - // onDropRejected(fileRejections, event) - // } - - // if (acceptedFiles.length > 0 && onDropAccepted) { - // onDropAccepted(acceptedFiles, event) - // } - }) + const customErrors = validator ? validator(file) : null; + + if (accepted && sizeMatch && !customErrors) { + acceptedFiles.push(file) + } else { + let errors = [acceptError, sizeError]; + + if (customErrors) { + errors = errors.concat(customErrors); + } + + fileRejections.push({ file, errors: errors.filter(e => e) }) + } + }); + + console.log('================='); + console.log(acceptedFiles); + console.log(fileRejections); + console.log('================='); + + // if ((!multiple && acceptedFiles.length > 1) || (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles)) { + // // Reject everything and empty accepted files + // acceptedFiles.forEach(file => { + // fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }) + // }) + // acceptedFiles.splice(0) + // } + + // dispatch({ + // acceptedFiles, + // fileRejections, + // 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({ type: 'reset' }) + dispatch({ type: 'reset' }) }, // eslint-disable-next-line react-hooks/exhaustive-deps [ - accept, - noDragEventsBubbling, // multiple, - // minSize, - // maxSize, + accept, + minSize, + maxSize, // maxFiles, // getFilesFromEvent, // onDrop, // onDropAccepted, // onDropRejected, - // validator + // noDragEventsBubbling, + // validator, ] ); @@ -373,11 +378,12 @@ export function useCSVReader() { const [config, setConfig] = useState({}); const [accept, setAccept] = useState(DEFAULT_ACCEPT); const [minSize, setMinSize] = useState(0); - const [maxSize, setMaxSize] = useState(0); + const [maxSize, setMaxSize] = useState(1000); const [className, setClassName] = useState(''); const [style, setStyle] = useState({}); const [progressBarColor, setProgressBarColor] = useState(''); const [removeButtonColor, setRemoveButtonColor] = useState(''); + const [validator, setValidator] = useState(null); const [noClick, setNoClick] = useState(false); const [noDrag, setNoDrag] = useState(false); const [noRemoveButton, setNoRemoveButton] = useState(false); @@ -404,6 +410,8 @@ export function useCSVReader() { setProgressBarColor, removeButtonColor, setRemoveButtonColor, + validator, + setValidator, noClick, setNoClick, noDrag, From faa17f04faf1d9ef15e49c1f7a3b4225b50136da Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 24 Nov 2021 00:13:09 +0700 Subject: [PATCH 041/140] Set multiple files --- src/useCSVReader.tsx | 157 +++++++++++++++++++++++++++---------------- src/utils.ts | 85 +++++++++++++---------- 2 files changed, 150 insertions(+), 92 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 9756718..3ac6470 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -17,6 +17,7 @@ import { isPropagationStopped, fileAccepted, fileMatchSize, + TOO_MANY_FILES_REJECTION, } from './utils'; const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; @@ -33,6 +34,7 @@ export interface Props { accept?: string; minSize?: number; maxSize?: number; + maxFiles?: number; className?: string; style?: any; progressBarColor?: string; @@ -51,6 +53,7 @@ export interface Props { isReset?: boolean; noKeyboard?: boolean; noDragEventsBubbling?: boolean; + multiple?: boolean; } // interface State { @@ -73,6 +76,8 @@ export interface Api { setMinSize?: () => void; maxSize?: number; setMaxSize?: () => void; + maxFiles?: number; + setMaxFiles?: () => void; className?: string; setClassName?: () => void; style?: any; @@ -100,6 +105,8 @@ export interface Api { setNoKeyboard?: () => void; noDragEventsBubbling?: boolean; setNoDragEventsBubbling?: () => void; + multiple?: boolean; + setMultiple?: () => void; } function useCSVReaderComponent(api: Api) { @@ -121,24 +128,39 @@ function useCSVReaderComponent(api: Api) { setMaxSize, validator, setValidator, + multiple, + setMultiple, + maxFiles, + setMaxFiles, } = CSVReader.api; const inputRef: any = useRef(null); const rootRef: any = useRef(null); - const dragTargetsRef = useRef([]) + const dragTargetsRef = useRef([]); const [state, dispatch] = useReducer(reducer, initialState); const { isFileDialogActive } = state; useEffect(() => { - const { config, accept, noDragEventsBubbling, minSize, maxSize, validator } = props; + const { + config, + accept, + noDragEventsBubbling, + minSize, + maxSize, + validator, + multiple, + maxFiles, + } = props; config && setConfig(config); accept && setAccept(accept); disabled && setDisabled(disabled); - noDragEventsBubbling && setNoDragEventsBubbling(noDragEventsBubbling) + noDragEventsBubbling && setNoDragEventsBubbling(noDragEventsBubbling); minSize && setMinSize(minSize); maxSize && setMaxSize(maxSize); validator && setValidator(validator); + multiple && setMultiple(multiple); + maxFiles && setMaxFiles(maxFiles); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -204,64 +226,71 @@ function useCSVReaderComponent(api: Api) { return childrenIsFunction() ? children(getProps) : children; }; - const onInputElementClick = useCallback(event => { - event.stopPropagation() + const onInputElementClick = useCallback((event) => { + event.stopPropagation(); }, []); const stopPropagation = (event: any) => { if (props.noDragEventsBubbling) { - event.stopPropagation() + event.stopPropagation(); } }; const onDropCb = useCallback( - event => { - event.preventDefault() + (event) => { + event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done - event.persist() - stopPropagation(event) - - dragTargetsRef.current = [] - + event.persist(); + stopPropagation(event); + + dragTargetsRef.current = []; + if (isEventWithFiles(event)) { if (isPropagationStopped(event) && !noDragEventsBubbling) { - return + return; } const acceptedFiles = [] as any; const fileRejections = [] as any; - Array.from(event.target.files).forEach(file => { - const [accepted, acceptError] = fileAccepted(file, accept) - const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize) + Array.from(event.target.files).forEach((file) => { + const [accepted, acceptError] = fileAccepted(file, accept); + const [sizeMatch, sizeError] = fileMatchSize( + file, + minSize, + maxSize, + ); const customErrors = validator ? validator(file) : null; if (accepted && sizeMatch && !customErrors) { - acceptedFiles.push(file) + acceptedFiles.push(file); } else { let errors = [acceptError, sizeError]; - + if (customErrors) { errors = errors.concat(customErrors); } - fileRejections.push({ file, errors: errors.filter(e => e) }) + fileRejections.push({ file, errors: errors.filter((e) => e) }); } }); - console.log('================='); + if ( + (!multiple && acceptedFiles.length > 1) || + (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) + ) { + // Reject everything and empty accepted files + acceptedFiles.forEach((file: any) => { + fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }); + }); + acceptedFiles.splice(0); + } + + console.log('11111111111111111111'); console.log(acceptedFiles); console.log(fileRejections); - console.log('================='); - - // if ((!multiple && acceptedFiles.length > 1) || (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles)) { - // // Reject everything and empty accepted files - // acceptedFiles.forEach(file => { - // fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }) - // }) - // acceptedFiles.splice(0) - // } - + console.log('11111111111111111111'); + // dispatch({ // acceptedFiles, // fileRejections, @@ -280,45 +309,53 @@ function useCSVReaderComponent(api: Api) { // onDropAccepted(acceptedFiles, event) // } } - dispatch({ type: 'reset' }) + dispatch({ type: 'reset' }); }, // eslint-disable-next-line react-hooks/exhaustive-deps [ - // multiple, + multiple, accept, minSize, maxSize, - // maxFiles, + maxFiles, + validator, // getFilesFromEvent, // onDrop, // onDropAccepted, // onDropRejected, // noDragEventsBubbling, - // validator, - ] + ], ); const getInputProps = useMemo( - () => ({ refKey = 'ref', onChange = () => {}, onClick = () => {}, ...rest } = {}) => { - const inputProps = { - accept, - // multiple, - type: 'file', - style: { display: 'none' }, - onChange: composeHandler(composeEventHandlers(onChange, onDropCb)), - onClick: composeHandler(composeEventHandlers(onClick, onInputElementClick)), - autoComplete: 'off', - tabIndex: -1, - [refKey]: inputRef - } - - return { - ...inputProps, + () => + ({ + refKey = 'ref', + onChange = () => {}, + onClick = () => {}, ...rest - } - }, + } = {}) => { + const inputProps = { + accept, + // multiple, + type: 'file', + style: { display: 'none' }, + onChange: composeHandler(composeEventHandlers(onChange, onDropCb)), + onClick: composeHandler( + composeEventHandlers(onClick, onInputElementClick), + ), + autoComplete: 'off', + tabIndex: -1, + [refKey]: inputRef, + }; + + return { + ...inputProps, + ...rest, + }; + }, // eslint-disable-next-line react-hooks/exhaustive-deps - [inputRef, accept, onDropCb, disabled] + [inputRef, accept, onDropCb, disabled], ); const composeHandler = (fn: any) => { @@ -347,9 +384,7 @@ function useCSVReaderComponent(api: Api) { return ( <> - + {childrenIsFunction() ? ( // button <>{renderChildren()} @@ -378,7 +413,8 @@ export function useCSVReader() { const [config, setConfig] = useState({}); const [accept, setAccept] = useState(DEFAULT_ACCEPT); const [minSize, setMinSize] = useState(0); - const [maxSize, setMaxSize] = useState(1000); + const [maxSize, setMaxSize] = useState(3000000); + const [maxFiles, setMaxFiles] = useState(1); const [className, setClassName] = useState(''); const [style, setStyle] = useState({}); const [progressBarColor, setProgressBarColor] = useState(''); @@ -392,6 +428,7 @@ export function useCSVReader() { const [disabled, setDisabled] = useState(false); const [noKeyboard, setNoKeyboard] = useState(false); const [noDragEventsBubbling, setNoDragEventsBubbling] = useState(false); + const [multiple, setMultiple] = useState(false); const api = { config, @@ -402,6 +439,8 @@ export function useCSVReader() { setMinSize, maxSize, setMaxSize, + maxFiles, + setMaxFiles, className, setClassName, style, @@ -428,6 +467,8 @@ export function useCSVReader() { setNoKeyboard, noDragEventsBubbling, setNoDragEventsBubbling, + multiple, + setMultiple, } as Api; const CSVReader = useCSVReaderComponent(api); diff --git a/src/utils.ts b/src/utils.ts index cb48be8..a6a11bb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,7 @@ 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 default function getSize(size: number) { const sizeKb = 1024; @@ -103,14 +104,14 @@ export function composeEventHandlers(...fns: any[]) { export function isEventWithFiles(event: any) { if (!event.dataTransfer) { - return !!event.target && !!event.target.files + 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' - ) + (type) => type === 'Files' || type === 'application/x-moz-file', + ); } /** @@ -128,69 +129,85 @@ export function accepts(file: any, acceptedFiles: any) { if (file && acceptedFiles) { const acceptedFilesArray = Array.isArray(acceptedFiles) ? acceptedFiles - : acceptedFiles.split(',') - const fileName = file.name || '' - const mimeType = (file.type || '').toLowerCase() - const baseMimeType = mimeType.replace(/\/.*$/, '') + : acceptedFiles.split(','); + const fileName = file.name || ''; + const mimeType = (file.type || '').toLowerCase(); + const baseMimeType = mimeType.replace(/\/.*$/, ''); return acceptedFilesArray.some((type: any) => { - const validType = type.trim().toLowerCase() + const validType = type.trim().toLowerCase(); if (validType.charAt(0) === '.') { - return fileName.toLowerCase().endsWith(validType) + return fileName.toLowerCase().endsWith(validType); } else if (validType.endsWith('/*')) { // This is something like a image/* mime type - return baseMimeType === validType.replace(/\/.*$/, '') + return baseMimeType === validType.replace(/\/.*$/, ''); } - return mimeType === validType - }) + return mimeType === validType; + }); } - return true + return true; } // File Errors export const getInvalidTypeRejectionErr = (accept: any) => { - accept = Array.isArray(accept) && accept.length === 1 ? accept[0] : accept - const messageSuffix = Array.isArray(accept) ? `one of ${accept.join(', ')}` : accept + accept = Array.isArray(accept) && accept.length === 1 ? accept[0] : accept; + const messageSuffix = Array.isArray(accept) + ? `one of ${accept.join(', ')}` + : accept; return { code: FILE_INVALID_TYPE, - message: `File type must be ${messageSuffix}` - } -} + message: `File type must be ${messageSuffix}`, + }; +}; // Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with // that MIME type will always be accepted export function fileAccepted(file: any, accept: any) { - const isAcceptable = file.type === 'application/x-moz-file' || accepts(file, accept) - return [isAcceptable, isAcceptable ? null : getInvalidTypeRejectionErr(accept)] + const isAcceptable = + file.type === 'application/x-moz-file' || accepts(file, accept); + return [ + isAcceptable, + isAcceptable ? null : getInvalidTypeRejectionErr(accept), + ]; } export function fileMatchSize(file: any, minSize: any, maxSize: any) { 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)] + 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] + return [true, null]; } function isDefined(value: any) { - return value !== undefined && value !== null + return value !== undefined && value !== null; } export const getTooLargeRejectionErr = (maxSize: any) => { return { code: FILE_TOO_LARGE, - message: `File is larger than ${maxSize} bytes` - } -} + message: `File is larger than ${maxSize} bytes`, + }; +}; export const getTooSmallRejectionErr = (minSize: any) => { return { code: FILE_TOO_SMALL, - message: `File is smaller than ${minSize} bytes` - } -} + message: `File is smaller than ${minSize} bytes`, + }; +}; + +export const TOO_MANY_FILES_REJECTION = { + code: TOO_MANY_FILES, + message: 'Too many files', +}; From 9ab505974cd63e716ee3437945b575b1707e409e Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 24 Nov 2021 17:41:59 +0700 Subject: [PATCH 042/140] Parse CSV --- src/useCSVReader.tsx | 27 ++++++++++++++++++------ supports/create-next-app/pages/index.tsx | 8 ++++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 3ac6470..1d85d62 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -41,6 +41,7 @@ export interface Props { removeButtonColor?: string; validator?: (file: any) => void; onDrop?: (data: Array>, file?: any) => void; + onFileUpload?: (data: Array>, file?: any, event?: any) => void; onFileLoad?: (data: Array>, file?: any) => void; onError?: (err: any, file: any, inputElem: any, reason: any) => void; onRemoveFile?: (data: null) => void; @@ -133,6 +134,7 @@ function useCSVReaderComponent(api: Api) { maxFiles, setMaxFiles, } = CSVReader.api; + const { onFileUpload } = props; const inputRef: any = useRef(null); const rootRef: any = useRef(null); @@ -291,11 +293,11 @@ function useCSVReaderComponent(api: Api) { console.log(fileRejections); console.log('11111111111111111111'); - // dispatch({ - // acceptedFiles, - // fileRejections, - // type: 'setFiles' - // }) + dispatch({ + acceptedFiles, + fileRejections, + type: 'setFiles' + }) // if (onDrop) { // onDrop(acceptedFiles, fileRejections, event) @@ -308,6 +310,10 @@ function useCSVReaderComponent(api: Api) { // if (acceptedFiles.length > 0 && onDropAccepted) { // onDropAccepted(acceptedFiles, event) // } + + if (onFileUpload) { + onFileUpload(acceptedFiles, fileRejections, event) + } } dispatch({ type: 'reset' }); }, @@ -319,11 +325,12 @@ function useCSVReaderComponent(api: Api) { maxSize, maxFiles, validator, - // getFilesFromEvent, + onFileUpload, // onDrop, // onDropAccepted, // onDropRejected, // noDragEventsBubbling, + // getFilesFromEvent, ], ); @@ -482,12 +489,12 @@ export function useCSVReader() { const initialState = { displayProgressBarStatus: 'none', isFileDialogActive: false, + acceptedFiles: [], // isFocused: false, // isDragActive: false, // isDragAccept: false, // isDragReject: false, // draggedFiles: [], - // acceptedFiles: [], // fileRejections: [], }; @@ -503,6 +510,12 @@ function reducer(state: any, action: any) { ...state, isFileDialogActive: false, }; + case 'setFiles': + return { + ...state, + acceptedFiles: action.acceptedFiles, + fileRejections: action.fileRejections + } default: return state; } diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 292b924..8bfaaf3 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -175,7 +175,13 @@ export default function Home() {
- + { + console.log('9999999999999999999999'); + console.log(acceptedFiles); + console.log('9999999999999999999999'); + }} + > {(getProps) => (
{ + onUploadAccepted={(acceptedFiles: any)=> { console.log('9999999999999999999999'); console.log(acceptedFiles); console.log('9999999999999999999999'); From 1f62c2991893614fe3d66df10c7c45259ff37f69 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 26 Nov 2021 18:03:39 +0700 Subject: [PATCH 049/140] Display file name --- src/useCSVReader.tsx | 17 +++++++++-- supports/create-next-app/pages/index.tsx | 39 +++++++++++++----------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 05289d0..d7da032 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -139,7 +139,7 @@ function useCSVReaderComponent(api: Api) { const dragTargetsRef = useRef([]); const [state, dispatch] = useReducer(reducer, initialState); - const { isFileDialogActive } = state; + const { isFileDialogActive, acceptedFile } = state; useEffect(() => { const { @@ -225,7 +225,9 @@ function useCSVReaderComponent(api: Api) { const renderChildren = () => { const { children } = props; - return childrenIsFunction() ? children(getProps) : children; + return childrenIsFunction() + ? children({ getProps, acceptedFile }) + : children; }; const onInputElementClick = useCallback((event) => { @@ -314,6 +316,11 @@ function useCSVReaderComponent(api: Api) { configs = Object.assign({}, config, configs); acceptedFiles.forEach((file: any) => { + dispatch({ + acceptedFile: file, + type: 'setFile', + }); + configs = { complete: config?.complete || config?.step @@ -537,6 +544,7 @@ const initialState = { displayProgressBarStatus: 'none', isFileDialogActive: false, acceptedFiles: [], + acceptedFile: null, // isFocused: false, // isDragActive: false, // isDragAccept: false, @@ -563,6 +571,11 @@ function reducer(state: any, action: any) { acceptedFiles: action.acceptedFiles, fileRejections: action.fileRejections, }; + case 'setFile': + return { + ...state, + acceptedFile: action.acceptedFile, + }; default: return state; } diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 06b6bc9..9c68f8b 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -108,9 +108,9 @@ export default function Home() { */}
- - - handleClick()}>readString + */} + {/* Download - - */} + {/* Download - - */} + {/* { return [ @@ -172,23 +172,28 @@ export default function Home() { } > Download - + */}
{ + onUploadAccepted={(results: any)=> { console.log('9999999999999999999999'); - console.log(acceptedFiles); + console.log(results); console.log('9999999999999999999999'); }} > - {(getProps) => ( - + {({ getProps, acceptedFile }) => ( + <> + +

+ {acceptedFile && acceptedFile.name} +

+ )}
From ff1b9495cd8aa9db78900343f6c5ceaf64ea2c61 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 28 Nov 2021 00:25:32 +0700 Subject: [PATCH 050/140] Refactor ProgressBar --- src/CSVReader.tsx | 10 ++++---- src/ProgressBar.tsx | 24 ++++++++++---------- src/useCSVReader.tsx | 54 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index 350dcf5..45b9c1a 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -553,8 +553,8 @@ export default class CSVReader extends React.Component< style?.dropFile?.progressBar || style?.progressBar, )} - progressBar={progressBar} - displayProgressBarStatus={displayProgressBarStatus} + percentage={progressBar} + display={displayProgressBarStatus} /> )} @@ -575,9 +575,9 @@ export default class CSVReader extends React.Component< style?.dropFile?.progressBar || style?.progressBar, )} - progressBar={progressBar} - displayProgressBarStatus={displayProgressBarStatus} - isButtonProgressBar + percentage={progressBar} + display={displayProgressBarStatus} + isButton /> )} diff --git a/src/ProgressBar.tsx b/src/ProgressBar.tsx index d52fdb7..680ba8d 100644 --- a/src/ProgressBar.tsx +++ b/src/ProgressBar.tsx @@ -10,11 +10,11 @@ const styles = { position: 'absolute', width: '80%', } as CSSProperties, - buttonProgressBar: { + button: { position: 'inherit', width: '100%', } as CSSProperties, - progressBarFill: { + fill: { backgroundColor: DEFAULT_PROGRESS_BAR_COLOR, borderRadius: 3, height: 10, @@ -24,18 +24,18 @@ const styles = { interface Props { style: any; - progressBar: number; - displayProgressBarStatus: string; - isButtonProgressBar?: boolean; + percentage: number; + display: string; + isButton?: boolean; } export default class ProgressBar extends React.Component { render() { const { style, - progressBar, - displayProgressBarStatus, - isButtonProgressBar, + percentage, + display, + isButton, } = this.props; return ( @@ -43,13 +43,13 @@ export default class ProgressBar extends React.Component { style={Object.assign( {}, styles.progressBar, - isButtonProgressBar && styles.buttonProgressBar, + isButton && styles.button, )} > diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index d7da032..4bc5e11 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -19,6 +19,7 @@ import { fileMatchSize, TOO_MANY_FILES_REJECTION, } from './utils'; +import ProgressBar from './ProgressBar'; const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; @@ -131,6 +132,8 @@ function useCSVReaderComponent(api: Api) { setMultiple, maxFiles, setMaxFiles, + noProgressBar, + setNoProgressBar, } = CSVReader.api; const { onUploadAccepted, onDropAccepted } = props; @@ -139,7 +142,7 @@ function useCSVReaderComponent(api: Api) { const dragTargetsRef = useRef([]); const [state, dispatch] = useReducer(reducer, initialState); - const { isFileDialogActive, acceptedFile } = state; + const { isFileDialogActive, acceptedFiles, acceptedFile, progressBarPercentage, displayProgressBar, progressBarColor } = state; useEffect(() => { const { @@ -152,7 +155,9 @@ function useCSVReaderComponent(api: Api) { multiple, maxFiles, noClick, + noProgressBar, } = props; + config && setConfig(config); accept && setAccept(accept); disabled && setDisabled(disabled); @@ -163,6 +168,7 @@ function useCSVReaderComponent(api: Api) { multiple && setMultiple(multiple); maxFiles && setMaxFiles(maxFiles); noClick && setNoClick(noClick); + noProgressBar && setNoProgressBar(noProgressBar); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -313,6 +319,7 @@ function useCSVReaderComponent(api: Api) { const errors: any = []; const meta: any = []; const reader = new window.FileReader(); + let percentage = 0; configs = Object.assign({}, config, configs); acceptedFiles.forEach((file: any) => { @@ -344,8 +351,11 @@ function useCSVReaderComponent(api: Api) { meta.push(row[0].meta); } if (config && config.preview) { - // percent = Math.round((data.length / config.preview) * 100); - // self.setState({ progressBar: percent }); + percentage = Math.round((data.length / config.preview) * 100); + dispatch({ + progressBarPercentage: percentage, + type: 'setProgressBarPercentage', + }); if (data.length === config.preview) { if (!onDropAccepted && onUploadAccepted) { onUploadAccepted(data, file); @@ -354,14 +364,17 @@ function useCSVReaderComponent(api: Api) { } } } else { - // const progress = row.meta.cursor; - // const newPercent = Math.round((progress / size) * 100); - // if (newPercent === percent) { - // return; - // } - // percent = newPercent; + const cursor = row.meta.cursor; + const newPercentage = Math.round((cursor / file.size) * 100); + if (newPercentage === percentage) { + return; + } + percentage = newPercentage; } - // self.setState({ progressBar: percent }); + dispatch({ + progressBarPercentage: percentage, + type: 'setProgressBarPercentage', + }); }, }; reader.onload = (e: any) => { @@ -448,7 +461,17 @@ function useCSVReaderComponent(api: Api) { {childrenIsFunction() ? ( // button - <>{renderChildren()} + <> + {renderChildren()} + {acceptedFiles && acceptedFile && !noProgressBar && ( + + )} + ) : ( // drop div
@@ -541,7 +564,9 @@ export function useCSVReader() { } const initialState = { - displayProgressBarStatus: 'none', + displayProgressBar: 'none', + progressBarPercentage: 0, + isFileDialogActive: false, acceptedFiles: [], acceptedFile: null, @@ -576,6 +601,11 @@ function reducer(state: any, action: any) { ...state, acceptedFile: action.acceptedFile, }; + case 'setProgressBarPercentage': + return { + ...state, + progressBarPercentage: action.progressBarPercentage, + }; default: return state; } From de51b6af60fb59e60bb58985525271d41b2da447 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 28 Nov 2021 18:27:46 +0700 Subject: [PATCH 051/140] Set progressbar active --- src/ProgressBar.tsx | 15 +++---------- src/useCSVReader.tsx | 53 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/ProgressBar.tsx b/src/ProgressBar.tsx index 680ba8d..035b7f6 100644 --- a/src/ProgressBar.tsx +++ b/src/ProgressBar.tsx @@ -8,7 +8,7 @@ const styles = { boxShadow: 'inset 0 1px 3px rgba(0, 0, 0, .2)', bottom: 14, position: 'absolute', - width: '80%', + width: '100%', } as CSSProperties, button: { position: 'inherit', @@ -31,20 +31,11 @@ interface Props { export default class ProgressBar extends React.Component { render() { - const { - style, - percentage, - display, - isButton, - } = this.props; + const { style, percentage, display, isButton } = this.props; return (
(api: Api) { const dragTargetsRef = useRef([]); const [state, dispatch] = useReducer(reducer, initialState); - const { isFileDialogActive, acceptedFiles, acceptedFile, progressBarPercentage, displayProgressBar, progressBarColor } = state; + const { + isFileDialogActive, + acceptedFiles, + acceptedFile, + progressBarPercentage, + displayProgressBar, + progressBarColor, + } = state; useEffect(() => { const { @@ -253,6 +260,11 @@ function useCSVReaderComponent(api: Api) { event.persist(); stopPropagation(event); + dispatch({ + progressBarPercentage: 0, + type: 'setProgressBarPercentage', + }); + dragTargetsRef.current = []; if (isEventWithFiles(event)) { @@ -302,6 +314,11 @@ function useCSVReaderComponent(api: Api) { type: 'setFiles', }); + dispatch({ + displayProgressBar: 'block', + type: 'setDisplayProgressBar', + }); + // if (onDrop) { // onDrop(acceptedFiles, fileRejections, event) // } @@ -351,7 +368,9 @@ function useCSVReaderComponent(api: Api) { meta.push(row[0].meta); } if (config && config.preview) { - percentage = Math.round((data.length / config.preview) * 100); + percentage = Math.round( + (data.length / config.preview) * 100, + ); dispatch({ progressBarPercentage: percentage, type: 'setProgressBarPercentage', @@ -365,7 +384,9 @@ function useCSVReaderComponent(api: Api) { } } else { const cursor = row.meta.cursor; - const newPercentage = Math.round((cursor / file.size) * 100); + const newPercentage = Math.round( + (cursor / file.size) * 100, + ); if (newPercentage === percentage) { return; } @@ -380,6 +401,16 @@ function useCSVReaderComponent(api: Api) { reader.onload = (e: any) => { PapaParse.parse(e.target.result, configs); }; + if (!noProgressBar) { + reader.onloadend = () => { + setTimeout(() => { + dispatch({ + displayProgressBar: 'none', + type: 'setDisplayProgressBar', + }); + }, 2000); + }; + } reader.readAsText(file, config.encoding || 'utf-8'); }); } @@ -468,7 +499,10 @@ function useCSVReaderComponent(api: Api) { isButton display={displayProgressBar} percentage={progressBarPercentage} - style={Object.assign({}, progressBarColor ? { backgroundColor: progressBarColor } : {})} + style={Object.assign( + {}, + progressBarColor ? { backgroundColor: progressBarColor } : {}, + )} /> )} @@ -566,6 +600,7 @@ export function useCSVReader() { const initialState = { displayProgressBar: 'none', progressBarPercentage: 0, + timeout: null, isFileDialogActive: false, acceptedFiles: [], @@ -606,6 +641,16 @@ function reducer(state: any, action: any) { ...state, progressBarPercentage: action.progressBarPercentage, }; + case 'setDisplayProgressBar': + return { + ...state, + displayProgressBar: action.displayProgressBar, + }; + case 'setTimeout': + return { + ...state, + timeout: action.timeout, + }; default: return state; } From 6ac4d002350f4f5150864bb10a241ffaa91354b2 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 29 Nov 2021 00:21:08 +0700 Subject: [PATCH 052/140] Export ProgressBar component --- supports/create-next-app/pages/index.tsx | 27 +++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 9c68f8b..d5bbf48 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -182,17 +182,38 @@ export default function Home() { console.log('9999999999999999999999'); }} > - {({ getProps, acceptedFile }) => ( + {({ getProps, acceptedFile, ProgressBar }) => ( <> +
-

+

{acceptedFile && acceptedFile.name} -

+
+ +
+ )} From fda76b9be81cdef3cdeeb92f2c63ae47e9c8dbd6 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 29 Nov 2021 00:21:20 +0700 Subject: [PATCH 053/140] Export ProgressBar component --- src/useCSVReader.tsx | 75 ++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 1a7a512..073dd1f 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -132,7 +132,7 @@ function useCSVReaderComponent(api: Api) { setMultiple, maxFiles, setMaxFiles, - noProgressBar, + // noProgressBar, setNoProgressBar, } = CSVReader.api; const { onUploadAccepted, onDropAccepted } = props; @@ -144,7 +144,7 @@ function useCSVReaderComponent(api: Api) { const [state, dispatch] = useReducer(reducer, initialState); const { isFileDialogActive, - acceptedFiles, + // acceptedFiles, acceptedFile, progressBarPercentage, displayProgressBar, @@ -236,10 +236,24 @@ function useCSVReaderComponent(api: Api) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [inputRef, isFileDialogActive, props.onFileDialogCancel]); + const Pro = () => { + return ( + + ); + } + const renderChildren = () => { const { children } = props; return childrenIsFunction() - ? children({ getProps, acceptedFile }) + ? children({ getProps, acceptedFile, ProgressBar: Pro }) : children; }; @@ -260,10 +274,7 @@ function useCSVReaderComponent(api: Api) { event.persist(); stopPropagation(event); - dispatch({ - progressBarPercentage: 0, - type: 'setProgressBarPercentage', - }); + setProgressBarPercentage(0); dragTargetsRef.current = []; @@ -314,10 +325,7 @@ function useCSVReaderComponent(api: Api) { type: 'setFiles', }); - dispatch({ - displayProgressBar: 'block', - type: 'setDisplayProgressBar', - }); + setDisplayProgressBar('block'); // if (onDrop) { // onDrop(acceptedFiles, fileRejections, event) @@ -371,10 +379,7 @@ function useCSVReaderComponent(api: Api) { percentage = Math.round( (data.length / config.preview) * 100, ); - dispatch({ - progressBarPercentage: percentage, - type: 'setProgressBarPercentage', - }); + setProgressBarPercentage(percentage); if (data.length === config.preview) { if (!onDropAccepted && onUploadAccepted) { onUploadAccepted(data, file); @@ -392,25 +397,19 @@ function useCSVReaderComponent(api: Api) { } percentage = newPercentage; } - dispatch({ - progressBarPercentage: percentage, - type: 'setProgressBarPercentage', - }); + setProgressBarPercentage(percentage); }, }; reader.onload = (e: any) => { PapaParse.parse(e.target.result, configs); }; - if (!noProgressBar) { - reader.onloadend = () => { - setTimeout(() => { - dispatch({ - displayProgressBar: 'none', - type: 'setDisplayProgressBar', - }); - }, 2000); - }; - } + // if (!noProgressBar) { + // reader.onloadend = () => { + // setTimeout(() => { + // setDisplayProgressBar('none'); + // }, 2000); + // }; + // } reader.readAsText(file, config.encoding || 'utf-8'); }); } @@ -487,6 +486,20 @@ function useCSVReaderComponent(api: Api) { [onClickCb], ); + const setProgressBarPercentage = (percentage: number) => { + dispatch({ + progressBarPercentage: percentage, + type: 'setProgressBarPercentage', + }); + }; + + const setDisplayProgressBar = (display: string) => { + dispatch({ + displayProgressBar: display, + type: 'setDisplayProgressBar', + }); + } + return ( <> @@ -494,7 +507,7 @@ function useCSVReaderComponent(api: Api) { // button <> {renderChildren()} - {acceptedFiles && acceptedFile && !noProgressBar && ( + {/* {acceptedFiles && acceptedFile && !noProgressBar && ( (api: Api) { progressBarColor ? { backgroundColor: progressBarColor } : {}, )} /> - )} + )} */} ) : ( // drop div From ebb5538422753c4c1b4affa9a755b74c602dd186 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 29 Nov 2021 18:29:44 +0700 Subject: [PATCH 054/140] Set ProgressBar style --- src/ProgressBar.tsx | 39 +++++++++-------- src/react-papaparse.ts | 2 +- src/useCSVReader.tsx | 55 +++++------------------- supports/create-next-app/pages/index.tsx | 48 +++++++++++---------- 4 files changed, 59 insertions(+), 85 deletions(-) diff --git a/src/ProgressBar.tsx b/src/ProgressBar.tsx index 035b7f6..3bb9e5e 100644 --- a/src/ProgressBar.tsx +++ b/src/ProgressBar.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties } from 'react'; +import React, { CSSProperties, useState, useEffect } from 'react'; const DEFAULT_PROGRESS_BAR_COLOR = '#659cef'; @@ -29,21 +29,26 @@ interface Props { isButton?: boolean; } -export default class ProgressBar extends React.Component { - render() { - const { style, percentage, display, isButton } = this.props; +export default function ProgressBar(props: Props) { + const { style, display, isButton } = props; + const [percentage, setPercentage] = useState(0); - return ( -
- -
- ); - } + console.log(style); + + useEffect(() => { + setPercentage(props.percentage); + }, [props.percentage]); + + return ( +
+ +
+ ); } diff --git a/src/react-papaparse.ts b/src/react-papaparse.ts index 7460c35..590e6ff 100644 --- a/src/react-papaparse.ts +++ b/src/react-papaparse.ts @@ -8,7 +8,7 @@ export const WORKERS_SUPPORTED = PapaParse.WORKERS_SUPPORTED; export const LocalChunkSize = PapaParse.LocalChunkSize; export const DefaultDelimiter = PapaParse.DefaultDelimiter; -export { default as CSVReader } from './CSVReader'; +// export { default as CSVReader } from './CSVReader'; export { default as CSVDownloader, LINK_TYPE, diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 073dd1f..fd176ae 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -38,7 +38,6 @@ export interface Props { maxFiles?: number; className?: string; style?: any; - progressBarColor?: string; removeButtonColor?: string; validator?: (file: any) => void; onDropAccepted?: (data: ParseResult, file?: any) => void; @@ -50,7 +49,6 @@ export interface Props { noClick?: boolean; noDrag?: boolean; noRemoveButton?: boolean; - noProgressBar?: boolean; isReset?: boolean; noKeyboard?: boolean; noDragEventsBubbling?: boolean; @@ -83,8 +81,6 @@ export interface Api { setClassName?: () => void; style?: any; setStyle?: () => void; - progressBarColor?: string; - setProgressBarColor?: () => void; removeButtonColor?: string; setRemoveButtonColor?: () => void; validator?: null; @@ -95,8 +91,6 @@ export interface Api { setNoDrag?: () => void; noRemoveButton?: boolean; setNoRemoveButton?: () => void; - noProgressBar?: boolean; - setNoProgressBar?: () => void; isReset?: boolean; setIsReset?: () => void; disabled?: boolean; @@ -132,8 +126,6 @@ function useCSVReaderComponent(api: Api) { setMultiple, maxFiles, setMaxFiles, - // noProgressBar, - setNoProgressBar, } = CSVReader.api; const { onUploadAccepted, onDropAccepted } = props; @@ -148,7 +140,6 @@ function useCSVReaderComponent(api: Api) { acceptedFile, progressBarPercentage, displayProgressBar, - progressBarColor, } = state; useEffect(() => { @@ -162,7 +153,6 @@ function useCSVReaderComponent(api: Api) { multiple, maxFiles, noClick, - noProgressBar, } = props; config && setConfig(config); @@ -175,7 +165,6 @@ function useCSVReaderComponent(api: Api) { multiple && setMultiple(multiple); maxFiles && setMaxFiles(maxFiles); noClick && setNoClick(noClick); - noProgressBar && setNoProgressBar(noProgressBar); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -236,19 +225,16 @@ function useCSVReaderComponent(api: Api) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [inputRef, isFileDialogActive, props.onFileDialogCancel]); - const Pro = () => { + const Pro = (props: any) => { return ( ); - } + }; const renderChildren = () => { const { children } = props; @@ -403,13 +389,11 @@ function useCSVReaderComponent(api: Api) { reader.onload = (e: any) => { PapaParse.parse(e.target.result, configs); }; - // if (!noProgressBar) { - // reader.onloadend = () => { - // setTimeout(() => { - // setDisplayProgressBar('none'); - // }, 2000); - // }; - // } + reader.onloadend = () => { + setTimeout(() => { + setDisplayProgressBar('none'); + }, 2000); + }; reader.readAsText(file, config.encoding || 'utf-8'); }); } @@ -498,27 +482,14 @@ function useCSVReaderComponent(api: Api) { displayProgressBar: display, type: 'setDisplayProgressBar', }); - } + }; return ( <> {childrenIsFunction() ? ( // button - <> - {renderChildren()} - {/* {acceptedFiles && acceptedFile && !noProgressBar && ( - - )} */} - + <>{renderChildren()} ) : ( // drop div
@@ -548,13 +519,11 @@ export function useCSVReader() { const [maxFiles, setMaxFiles] = useState(1); const [className, setClassName] = useState(''); const [style, setStyle] = useState({}); - const [progressBarColor, setProgressBarColor] = useState(''); const [removeButtonColor, setRemoveButtonColor] = useState(''); const [validator, setValidator] = useState(null); const [noClick, setNoClick] = useState(false); const [noDrag, setNoDrag] = useState(false); const [noRemoveButton, setNoRemoveButton] = useState(false); - const [noProgressBar, setNoProgressBar] = useState(false); const [isReset, setIsReset] = useState(false); const [disabled, setDisabled] = useState(false); const [noKeyboard, setNoKeyboard] = useState(false); @@ -576,8 +545,6 @@ export function useCSVReader() { setClassName, style, setStyle, - progressBarColor, - setProgressBarColor, removeButtonColor, setRemoveButtonColor, validator, @@ -588,8 +555,6 @@ export function useCSVReader() { setNoDrag, noRemoveButton, setNoRemoveButton, - noProgressBar, - setNoProgressBar, isReset, setIsReset, disabled, diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index d5bbf48..0ffe2ae 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -183,38 +183,42 @@ export default function Home() { }} > {({ getProps, acceptedFile, ProgressBar }) => ( - <>
-
- {acceptedFile && acceptedFile.name} + +
+ {acceptedFile && acceptedFile.name} +
- +
- - )}
From 4aca4b0479462999f1e39c93a651146b6db70bca Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 29 Nov 2021 23:22:58 +0700 Subject: [PATCH 055/140] Add className props to ProgressBar --- src/ProgressBar.tsx | 14 +++++++------- src/useCSVReader.tsx | 3 ++- supports/create-next-app/pages/index.tsx | 3 +-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ProgressBar.tsx b/src/ProgressBar.tsx index 3bb9e5e..2a1b367 100644 --- a/src/ProgressBar.tsx +++ b/src/ProgressBar.tsx @@ -7,7 +7,7 @@ const styles = { borderRadius: 3, boxShadow: 'inset 0 1px 3px rgba(0, 0, 0, .2)', bottom: 14, - position: 'absolute', + // position: 'absolute', width: '100%', } as CSSProperties, button: { @@ -23,28 +23,28 @@ const styles = { }; interface Props { - style: any; + style?: any; + className?: string; percentage: number; display: string; isButton?: boolean; } export default function ProgressBar(props: Props) { - const { style, display, isButton } = props; + const { style, className, display, isButton } = props; const [percentage, setPercentage] = useState(0); - console.log(style); - useEffect(() => { setPercentage(props.percentage); }, [props.percentage]); return (
(api: Api) { isButton display={displayProgressBar} percentage={progressBarPercentage} - style={props.style ? props.style : {}} + style={props.style} + className={props.className} /> ); }; diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 0ffe2ae..ecadf1b 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -209,8 +209,7 @@ export default function Home() { border: '1px solid #ccc', height: 45, lineHeight: 2.5, - margin: '5px 0 5px 0', - padding: '3px 0 0 13px', + paddingLeft: 10, width: '80%', }} > From bd6f351bf9a2b75edc903e94b087104210a3cac4 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Tue, 30 Nov 2021 13:55:21 +0700 Subject: [PATCH 056/140] Fix ProgressBar className --- src/ProgressBar.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ProgressBar.tsx b/src/ProgressBar.tsx index 2a1b367..ec4c377 100644 --- a/src/ProgressBar.tsx +++ b/src/ProgressBar.tsx @@ -7,8 +7,8 @@ const styles = { borderRadius: 3, boxShadow: 'inset 0 1px 3px rgba(0, 0, 0, .2)', bottom: 14, - // position: 'absolute', width: '100%', + // position: 'absolute', } as CSSProperties, button: { position: 'inherit', @@ -39,15 +39,13 @@ export default function ProgressBar(props: Props) { }, [props.percentage]); return ( -
+
); From 9d91586a573388154d569d5071378b5fc009cb16 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Tue, 30 Nov 2021 18:04:14 +0700 Subject: [PATCH 057/140] Refactor functions name --- src/useCSVReader.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 777fe09..644b479 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -225,7 +225,7 @@ function useCSVReaderComponent(api: Api) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [inputRef, isFileDialogActive, props.onFileDialogCancel]); - const Pro = (props: any) => { + const ProgressBarComponent = (props: any) => { return ( (api: Api) { const renderChildren = () => { const { children } = props; return childrenIsFunction() - ? children({ getProps, acceptedFile, ProgressBar: Pro }) + ? children({ getProps: getButtonProps, acceptedFile, ProgressBar: ProgressBarComponent }) : children; }; @@ -451,7 +451,7 @@ function useCSVReaderComponent(api: Api) { return disabled ? null : fn; }; - const getProps = useMemo( + const getButtonProps = useMemo( () => ({ onClick = () => {}, ...rest } = {}) => ({ onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), @@ -461,7 +461,7 @@ function useCSVReaderComponent(api: Api) { [onClickCb], ); - const getRootProps = useMemo( + const getDropZoneProps = useMemo( () => ({ onClick = () => {}, ...rest } = {}) => ({ onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), @@ -493,7 +493,7 @@ function useCSVReaderComponent(api: Api) { <>{renderChildren()} ) : ( // drop div -
+
{props.children}
)} From e30887391b3b941574a72118754d89f75a33d7cc Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 1 Dec 2021 18:30:11 +0700 Subject: [PATCH 058/140] Fix format --- demo/CSVReader2.js | 2 -- src/useCSVReader.tsx | 35 +++++++++++++++-------- supports/create-next-app/pages/index.tsx | 36 ++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/demo/CSVReader2.js b/demo/CSVReader2.js index 182bd20..d05cb25 100644 --- a/demo/CSVReader2.js +++ b/demo/CSVReader2.js @@ -25,8 +25,6 @@ export default class CSVReader2 extends Component { Drop CSV file here or click to upload. diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 644b479..3a93cbb 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -60,7 +60,6 @@ export interface Props { // progressBar: number; // displayProgressBarStatus: string; // file: any; -// timeout: any; // files: any; // removeIconColor: string; // isCanceled: boolean; @@ -127,7 +126,7 @@ function useCSVReaderComponent(api: Api) { maxFiles, setMaxFiles, } = CSVReader.api; - const { onUploadAccepted, onDropAccepted } = props; + const { onUploadAccepted, onDropAccepted, noDrag } = props; const inputRef: any = useRef(null); const rootRef: any = useRef(null); @@ -239,8 +238,21 @@ function useCSVReaderComponent(api: Api) { const renderChildren = () => { const { children } = props; + + React.Children.forEach(children({ getButtonProps, acceptedFile, ProgressBar: ProgressBarComponent }), child => { + console.log('99999999999999999999999999'); + if (child) { + console.log((child as any).ref); + } + console.log('99999999999999999999999999'); + }); + + // return childrenIsFunction() + // ? children({ getButtonProps, acceptedFile, ProgressBar: ProgressBarComponent }) + // : children; + return childrenIsFunction() - ? children({ getProps: getButtonProps, acceptedFile, ProgressBar: ProgressBarComponent }) + ? children({ getDropzoneProps, acceptedFile, ProgressBar: ProgressBarComponent }) : children; }; @@ -451,6 +463,10 @@ function useCSVReaderComponent(api: Api) { return disabled ? null : fn; }; + const composeDragHandler = (fn: any) => { + return noDrag ? null : composeHandler(fn) + } + const getButtonProps = useMemo( () => ({ onClick = () => {}, ...rest } = {}) => ({ @@ -461,10 +477,11 @@ function useCSVReaderComponent(api: Api) { [onClickCb], ); - const getDropZoneProps = useMemo( + const getDropzoneProps = useMemo( () => - ({ onClick = () => {}, ...rest } = {}) => ({ + ({ onClick = () => {}, onDrop = () => {}, ...rest } = {}) => ({ onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), + onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), ...rest, }), // eslint-disable-next-line react-hooks/exhaustive-deps @@ -493,7 +510,7 @@ function useCSVReaderComponent(api: Api) { <>{renderChildren()} ) : ( // drop div -
+
{props.children}
)} @@ -579,7 +596,6 @@ export function useCSVReader() { const initialState = { displayProgressBar: 'none', progressBarPercentage: 0, - timeout: null, isFileDialogActive: false, acceptedFiles: [], @@ -625,11 +641,6 @@ function reducer(state: any, action: any) { ...state, displayProgressBar: action.displayProgressBar, }; - case 'setTimeout': - return { - ...state, - timeout: action.timeout, - }; default: return state; } diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index ecadf1b..6899105 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -174,7 +174,7 @@ export default function Home() { Download */}
-
+ {/*
{ console.log('9999999999999999999999'); @@ -182,7 +182,7 @@ export default function Home() { console.log('9999999999999999999999'); }} > - {({ getProps, acceptedFile, ProgressBar }) => ( + {({ getButtonProps, acceptedFile, ProgressBar }) => (
*/} +
+ { + console.log('9999999999999999999999'); + console.log(results); + console.log('9999999999999999999999'); + }} + > + {({ getDropzoneProps }) => ( +
+ Drop CSV file here or click to upload. +
+ )} +
) From b215368149a68ac97d3104da111ee96f9eba7405 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 5 Dec 2021 00:17:44 +0700 Subject: [PATCH 059/140] Check children is button or dropzone --- src/useCSVReader.tsx | 19 +++----------- supports/create-next-app/pages/index.tsx | 32 +----------------------- 2 files changed, 5 insertions(+), 46 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 3a93cbb..dbb3a2b 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -237,22 +237,10 @@ function useCSVReaderComponent(api: Api) { }; const renderChildren = () => { - const { children } = props; + const { children, onUploadAccepted } = props; - React.Children.forEach(children({ getButtonProps, acceptedFile, ProgressBar: ProgressBarComponent }), child => { - console.log('99999999999999999999999999'); - if (child) { - console.log((child as any).ref); - } - console.log('99999999999999999999999999'); - }); - - // return childrenIsFunction() - // ? children({ getButtonProps, acceptedFile, ProgressBar: ProgressBarComponent }) - // : children; - return childrenIsFunction() - ? children({ getDropzoneProps, acceptedFile, ProgressBar: ProgressBarComponent }) + ? (onUploadAccepted ? children({ getButtonProps, acceptedFile, ProgressBar: ProgressBarComponent }) : children({ getDropzoneProps, acceptedFile, ProgressBar: ProgressBarComponent })) : children; }; @@ -510,7 +498,8 @@ function useCSVReaderComponent(api: Api) { <>{renderChildren()} ) : ( // drop div -
+ //
+
{props.children}
)} diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 6899105..369f990 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -174,7 +174,7 @@ export default function Home() { Download */}
- {/*
+
{ console.log('9999999999999999999999'); @@ -220,36 +220,6 @@ export default function Home() {
)} -
*/} -
- { - console.log('9999999999999999999999'); - console.log(results); - console.log('9999999999999999999999'); - }} - > - {({ getDropzoneProps }) => ( -
- Drop CSV file here or click to upload. -
- )} -
) From 28c6563c6a15c6494a97b762497686a416f39797 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 8 Dec 2021 00:35:55 +0700 Subject: [PATCH 060/140] Add onDragOver event --- src/useCSVReader.tsx | 31 +++++++++++++++++++++--- supports/create-next-app/pages/index.tsx | 30 ++++++++++++++++++++++- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index dbb3a2b..b47193e 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -45,6 +45,7 @@ export interface Props { onError?: (err: any, file: any, inputElem: any, reason: any) => void; onRemove?: (data: null) => void; onFileDialogCancel?: () => void; + onDragOver?: (event?: any) => void; disabled?: boolean; noClick?: boolean; noDrag?: boolean; @@ -126,7 +127,7 @@ function useCSVReaderComponent(api: Api) { maxFiles, setMaxFiles, } = CSVReader.api; - const { onUploadAccepted, onDropAccepted, noDrag } = props; + const { onUploadAccepted, onDropAccepted, onDragOver, noDrag } = props; const inputRef: any = useRef(null); const rootRef: any = useRef(null); @@ -254,8 +255,30 @@ function useCSVReaderComponent(api: Api) { } }; + const onDragOverCb = useCallback( + event => { + event.preventDefault() + event.persist() + stopPropagation(event) + + const hasFiles = isEventWithFiles(event); + if (hasFiles && event.dataTransfer) { + try { + event.dataTransfer.dropEffect = 'copy' + } catch {} /* eslint-disable-line no-empty */ + } + + if (hasFiles && onDragOver) { + onDragOver(event) + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ) + const onDropCb = useCallback( (event) => { + alert('Hi'); event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done event.persist(); @@ -467,13 +490,15 @@ function useCSVReaderComponent(api: Api) { const getDropzoneProps = useMemo( () => - ({ onClick = () => {}, onDrop = () => {}, ...rest } = {}) => ({ + ({ onClick = () => {}, onDrop = () => {}, onDragOver = () => {}, ...rest } = {}) => ({ onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), + // onDragEnter: composeDragHandler(composeEventHandlers(onDragEnter, onDragEnterCb)), + onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)), ...rest, }), // eslint-disable-next-line react-hooks/exhaustive-deps - [onClickCb], + [onClickCb, onDropCb, onDragOverCb], ); const setProgressBarPercentage = (percentage: number) => { diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 369f990..08c67bd 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -175,7 +175,7 @@ export default function Home() { */}
- { console.log('9999999999999999999999'); console.log(results); @@ -219,6 +219,34 @@ export default function Home() {
)} +
*/} + + { + console.log('9999999999999999999999'); + console.log(results); + console.log('9999999999999999999999'); + }} + > + {({ getDropzoneProps, acceptedFile }) => ( +
+ {acceptedFile && acceptedFile.name} +
+ )}
From 58c889eddd9639f66a440172ff30092ea387d7bf Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 19 Dec 2021 16:12:17 +0700 Subject: [PATCH 061/140] Add Drop onFocus and onBlur --- demo/CSVReader2.js | 5 +- src/useCSVReader.tsx | 742 +++++++++++++++++-------------------------- src/utils.ts | 36 +++ 3 files changed, 322 insertions(+), 461 deletions(-) diff --git a/demo/CSVReader2.js b/demo/CSVReader2.js index d05cb25..3a79458 100644 --- a/demo/CSVReader2.js +++ b/demo/CSVReader2.js @@ -22,10 +22,7 @@ export default class CSVReader2 extends Component { return ( <>
Click and Drag Upload
- + Drop CSV file here or click to upload. diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index b47193e..8f2b9c6 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -3,25 +3,21 @@ import React, { useReducer, useCallback, useMemo, - useEffect, useState, ReactNode, useRef, + useEffect, } from 'react'; -import PapaParse, { ParseResult } from 'papaparse'; +import { ParseResult } from 'papaparse'; import { CustomConfig } from './model'; import { - isIeOrEdge, composeEventHandlers, + onDocumentDragOver, isEventWithFiles, - isPropagationStopped, - fileAccepted, - fileMatchSize, - TOO_MANY_FILES_REJECTION, } from './utils'; import ProgressBar from './ProgressBar'; -const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; +// const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; // const cssProperty = { // inputFile: { @@ -31,29 +27,14 @@ const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; export interface Props { children: (fn: any) => void | ReactNode; - config?: CustomConfig; - accept?: string; - minSize?: number; - maxSize?: number; - maxFiles?: number; - className?: string; - style?: any; - removeButtonColor?: string; - validator?: (file: any) => void; - onDropAccepted?: (data: ParseResult, file?: any) => void; onUploadAccepted?: (data: ParseResult, file?: any, event?: any) => void; - onError?: (err: any, file: any, inputElem: any, reason: any) => void; - onRemove?: (data: null) => void; - onFileDialogCancel?: () => void; - onDragOver?: (event?: any) => void; + onDragLeave?: (event?: any) => void; disabled?: boolean; noClick?: boolean; noDrag?: boolean; - noRemoveButton?: boolean; - isReset?: boolean; - noKeyboard?: boolean; noDragEventsBubbling?: boolean; - multiple?: boolean; + preventDropOnDocument?: boolean; + noKeyboard?: boolean; } // interface State { @@ -68,163 +49,62 @@ export interface Props { export interface Api { config?: CustomConfig; - setConfig?: () => void; - accept?: string; - setAccept?: () => void; - minSize?: number; - setMinSize?: () => void; - maxSize?: number; - setMaxSize?: () => void; - maxFiles?: number; - setMaxFiles?: () => void; - className?: string; - setClassName?: () => void; - style?: any; - setStyle?: () => void; - removeButtonColor?: string; - setRemoveButtonColor?: () => void; - validator?: null; - setValidator?: () => void; - noClick?: boolean; - setNoClick?: () => void; - noDrag?: boolean; - setNoDrag?: () => void; - noRemoveButton?: boolean; - setNoRemoveButton?: () => void; - isReset?: boolean; - setIsReset?: () => void; disabled?: boolean; setDisabled?: () => void; - noKeyboard?: boolean; - setNoKeyboard?: () => void; - noDragEventsBubbling?: boolean; - setNoDragEventsBubbling?: () => void; - multiple?: boolean; - setMultiple?: () => void; } function useCSVReaderComponent(api: Api) { const CSVReaderComponent = (props: Props) => { + // Use variables from api as global + const { accept, disabled, setDisabled, noDrag, setNoDrag } = CSVReader.api; + const { - config, - setConfig, - accept, - setAccept, - noClick, - setNoClick, - disabled, - setDisabled, - noDragEventsBubbling, - setNoDragEventsBubbling, - minSize, - setMinSize, - maxSize, - setMaxSize, - validator, - setValidator, - multiple, - setMultiple, - maxFiles, - setMaxFiles, - } = CSVReader.api; - const { onUploadAccepted, onDropAccepted, onDragOver, noDrag } = props; + noDragEventsBubbling = false, + preventDropOnDocument = true, + noKeyboard = false, + onDragLeave, + } = props; const inputRef: any = useRef(null); const rootRef: any = useRef(null); - const dragTargetsRef = useRef([]); const [state, dispatch] = useReducer(reducer, initialState); + + console.log(dispatch); + const { - isFileDialogActive, - // acceptedFiles, acceptedFile, progressBarPercentage, displayProgressBar, + + draggedFiles, } = state; + // global + useEffect(() => { - const { - config, - accept, - noDragEventsBubbling, - minSize, - maxSize, - validator, - multiple, - maxFiles, - noClick, - } = props; - - config && setConfig(config); - accept && setAccept(accept); + const { disabled, noDrag } = props; disabled && setDisabled(disabled); - noDragEventsBubbling && setNoDragEventsBubbling(noDragEventsBubbling); - minSize && setMinSize(minSize); - maxSize && setMaxSize(maxSize); - validator && setValidator(validator); - multiple && setMultiple(multiple); - maxFiles && setMaxFiles(maxFiles); - noClick && setNoClick(noClick); + noDrag && setNoDrag(noDrag); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const childrenIsFunction = () => { - return typeof props.children === 'function'; - }; - - // Fn for opening the file dialog programmatically - const openFileDialog = useCallback(() => { - // if (inputRef.current && state.displayProgressBarStatus) { - if (inputRef.current) { - dispatch({ type: 'openDialog' }); - inputRef.current.value = null; - inputRef.current.click(); - } - }, [dispatch]); - - // Cb to open the file dialog when click occurs on the dropzone - const onClickCb = useCallback(() => { - if (noClick) { - return; - } + const renderChildren = () => { + const { children, onUploadAccepted } = props; - // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() - // to ensure React can handle state changes - if (isIeOrEdge()) { - setTimeout(openFileDialog, 0); - } else { - openFileDialog(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [inputRef, noClick]); - - // Update file dialog active state when the window is focused on - const onWindowFocus = () => { - // Execute the timeout only if the file dialog is opened in the browser - if (isFileDialogActive) { - setTimeout(() => { - if (inputRef.current) { - const { files } = inputRef.current; - - if (!files.length) { - dispatch({ type: 'closeDialog' }); - if (typeof props.onFileDialogCancel === 'function') { - props.onFileDialogCancel(); - } - } - } - }, 300); - } + return onUploadAccepted + ? children({ + getButtonProps, + acceptedFile, + ProgressBar: ProgressBarComponent, + }) + : children({ + getDropzoneProps, + acceptedFile, + ProgressBar: ProgressBarComponent, + }); }; - useEffect(() => { - window.addEventListener('focus', onWindowFocus, false); - return () => { - window.removeEventListener('focus', onWindowFocus, false); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [inputRef, isFileDialogActive, props.onFileDialogCancel]); - const ProgressBarComponent = (props: any) => { return ( (api: Api) { ); }; - const renderChildren = () => { - const { children, onUploadAccepted } = props; - - return childrenIsFunction() - ? (onUploadAccepted ? children({ getButtonProps, acceptedFile, ProgressBar: ProgressBarComponent }) : children({ getDropzoneProps, acceptedFile, ProgressBar: ProgressBarComponent })) - : children; - }; + const onDropCb = useCallback( + (event) => { + allowDrop(event); - const onInputElementClick = useCallback((event) => { - event.stopPropagation(); - }, []); + alert('OnDrop'); + console.log(draggedFiles); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + + // Cb to open the file dialog when click occurs on the dropzone + const onClickCb = useCallback(() => { + // if (noClick) { + // return; + // } + // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() + // to ensure React can handle state changes + // if (isIeOrEdge()) { + // setTimeout(openFileDialog, 0); + // } else { + // openFileDialog(); + // } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + inputRef, + // noClick, + ]); + + const onDragOverCb = useCallback( + (event) => { + allowDrop(event); + + // const hasFiles = isEventWithFiles(event); + // if (hasFiles && event.dataTransfer) { + // try { + // event.dataTransfer.dropEffect = 'copy' + // } catch {} /* eslint-disable-line no-empty */ + // } + + // if (hasFiles && onDragOver) { + // onDragOver(event) + // } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + + const onDragEnterCb = useCallback( + (event) => { + allowDrop(event); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); const stopPropagation = (event: any) => { - if (props.noDragEventsBubbling) { + if (noDragEventsBubbling) { event.stopPropagation(); } }; - const onDragOverCb = useCallback( - event => { - event.preventDefault() - event.persist() - stopPropagation(event) - - const hasFiles = isEventWithFiles(event); - if (hasFiles && event.dataTransfer) { - try { - event.dataTransfer.dropEffect = 'copy' - } catch {} /* eslint-disable-line no-empty */ + const allowDrop = (event: any) => { + event.preventDefault(event); + // Persist here because we need the event later after getFilesFromEvent() is done + event.persist(); + stopPropagation(event); + }; + + // Fn for opening the file dialog programmatically + const openFileDialog = useCallback(() => { + // if (inputRef.current && state.displayProgressBarStatus) { + if (inputRef.current) { + dispatch({ type: 'openDialog' }); + inputRef.current.value = null; + inputRef.current.click(); + } + }, [dispatch]); + + // ===================== + + // Dropzone + + // Update focus state for the dropzone + const onFocusCb = useCallback(() => { + dispatch({ type: 'focus' }); + }, []); + const onBlurCb = useCallback(() => { + dispatch({ type: 'blur' }); + }, []); + + const onDragLeaveCb = useCallback( + (event: any) => { + allowDrop(event); + + // Only deactivate once the dropzone and all children have been left + const targets = dragTargetsRef.current.filter( + (target) => 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) + const targetIdx = targets.indexOf(event.target as never); + if (targetIdx !== -1) { + targets.splice(targetIdx, 1); + } + dragTargetsRef.current = targets; + if (targets.length > 0) { + return; } - - if (hasFiles && onDragOver) { - onDragOver(event) + + dispatch({ + isDragActive: false, + type: 'setDraggedFiles', + draggedFiles: [], + }); + + if (isEventWithFiles(event) && onDragLeave) { + onDragLeave(event); } }, // eslint-disable-next-line react-hooks/exhaustive-deps - [] - ) - - const onDropCb = useCallback( - (event) => { - alert('Hi'); - event.preventDefault(); - // Persist here because we need the event later after getFilesFromEvent() is done - event.persist(); - stopPropagation(event); - - setProgressBarPercentage(0); - - dragTargetsRef.current = []; - - if (isEventWithFiles(event)) { - if (isPropagationStopped(event) && !noDragEventsBubbling) { - return; - } - - const acceptedFiles = [] as any; - const fileRejections = [] as any; - - Array.from(event.target.files).forEach((file) => { - const [accepted, acceptError] = fileAccepted(file, accept); - const [sizeMatch, sizeError] = fileMatchSize( - file, - minSize, - maxSize, - ); - const customErrors = validator ? validator(file) : null; - - if (accepted && sizeMatch && !customErrors) { - acceptedFiles.push(file); - } else { - let errors = [acceptError, sizeError]; - - if (customErrors) { - errors = errors.concat(customErrors); - } - - fileRejections.push({ file, errors: errors.filter((e) => e) }); - } - }); + [rootRef, onDragLeave, noDragEventsBubbling], + ); - if ( - (!multiple && acceptedFiles.length > 1) || - (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) - ) { - // Reject everything and empty accepted files - acceptedFiles.forEach((file: any) => { - fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }); - }); - acceptedFiles.splice(0); - } - - dispatch({ - acceptedFiles, - fileRejections, - type: 'setFiles', - }); + // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone + const onKeyDownCb = useCallback( + (event: any) => { + // Ignore keyboard events bubbling up the DOM tree + if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) { + return; + } - setDisplayProgressBar('block'); - - // if (onDrop) { - // onDrop(acceptedFiles, fileRejections, event) - // } - - // if (fileRejections.length > 0 && onDropRejected) { - // onDropRejected(fileRejections, event) - // } - - // if (acceptedFiles.length > 0 && onDropAccepted) { - // onDropAccepted(acceptedFiles, event) - // } - - let configs = {} as any; - const data: any = []; - const errors: any = []; - const meta: any = []; - const reader = new window.FileReader(); - let percentage = 0; - - configs = Object.assign({}, config, configs); - acceptedFiles.forEach((file: any) => { - dispatch({ - acceptedFile: file, - type: 'setFile', - }); - - configs = { - complete: - config?.complete || config?.step - ? config.complete - : () => { - const obj = { data, errors, meta }; - if (!onDropAccepted && onUploadAccepted) { - onUploadAccepted(obj, file); - } else if (onDropAccepted && !onUploadAccepted) { - onDropAccepted(obj, file); - } - }, - step: config?.step - ? config.step - : (row: any) => { - data.push(row.data); - if (row.errors.length > 0) { - errors.push(row.errors); - } - if (row.length > 0) { - meta.push(row[0].meta); - } - if (config && config.preview) { - percentage = Math.round( - (data.length / config.preview) * 100, - ); - setProgressBarPercentage(percentage); - if (data.length === config.preview) { - if (!onDropAccepted && onUploadAccepted) { - onUploadAccepted(data, file); - } else if (onDropAccepted && !onUploadAccepted) { - onDropAccepted(data, file); - } - } - } else { - const cursor = row.meta.cursor; - const newPercentage = Math.round( - (cursor / file.size) * 100, - ); - if (newPercentage === percentage) { - return; - } - percentage = newPercentage; - } - setProgressBarPercentage(percentage); - }, - }; - reader.onload = (e: any) => { - PapaParse.parse(e.target.result, configs); - }; - reader.onloadend = () => { - setTimeout(() => { - setDisplayProgressBar('none'); - }, 2000); - }; - reader.readAsText(file, config.encoding || 'utf-8'); - }); + if (event.keyCode === 32 || event.keyCode === 13) { + event.preventDefault(); + openFileDialog(); } - dispatch({ type: 'reset' }); }, // eslint-disable-next-line react-hooks/exhaustive-deps + [rootRef, inputRef], + ); + + const getDropzoneProps = useMemo( + () => + ({ + onClick = () => {}, + onDragOver = () => {}, + onDrop = () => {}, + onDragEnter = () => {}, + onDragLeave = () => {}, + onKeyDown = () => {}, + onFocus = () => {}, + onBlur = () => {}, + refKey = rootRef, + ...rest + } = {}) => ({ + onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), + onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), + onDragEnter: composeDragHandler( + composeEventHandlers(onDragEnter, onDragEnterCb), + ), + onDragOver: composeDragHandler( + composeEventHandlers(onDragOver, onDragOverCb), + ), + onDragLeave: composeDragHandler( + composeEventHandlers(onDragLeave, onDragLeaveCb), + ), + onKeyDown: composeKeyboardHandler( + composeEventHandlers(onKeyDown, onKeyDownCb), + ), + onFocus: composeKeyboardHandler( + composeEventHandlers(onFocus, onFocusCb), // Done + ), + onBlur: composeKeyboardHandler( + composeEventHandlers(onBlur, onBlurCb), // Done + ), + [refKey]: rootRef, + ...rest, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps [ - multiple, - accept, - minSize, - maxSize, - maxFiles, - validator, - onUploadAccepted, - onDropAccepted, - // onDropRejected, - // noDragEventsBubbling, - // getFilesFromEvent, + rootRef, + onKeyDownCb, + onFocusCb, + onBlurCb, + onClickCb, + onDragEnterCb, + onDragOverCb, + onDragLeaveCb, + onDropCb, + noKeyboard, + noDrag, + disabled, ], ); + const dragTargetsRef = useRef([]); + const onDocumentDrop = (event: any) => { + 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(() => { + if (preventDropOnDocument) { + document.addEventListener('dragover', onDocumentDragOver, true); + document.addEventListener('drop', onDocumentDrop, true); + } + + return () => { + if (preventDropOnDocument) { + document.removeEventListener('dragover', onDocumentDragOver); + document.removeEventListener('drop', onDocumentDrop); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [rootRef, preventDropOnDocument]); + + const composeDragHandler = (fn: any) => { + return noDrag ? null : composeHandler(fn); + }; + + const composeKeyboardHandler = (fn: any) => { + return noKeyboard ? null : composeHandler(fn); + }; + + // ===================== + + // Button + const getButtonProps = useMemo( + () => + ({ /*onClick = () => {},*/ ...rest } = {}) => ({ + // onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), + ...rest, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [ + /*onClickCb*/ + ], + ); + // ===================== + + // ======= Input ======= + const getInputProps = useMemo( () => ({ @@ -474,60 +398,16 @@ function useCSVReaderComponent(api: Api) { return disabled ? null : fn; }; - const composeDragHandler = (fn: any) => { - return noDrag ? null : composeHandler(fn) - } - - const getButtonProps = useMemo( - () => - ({ onClick = () => {}, ...rest } = {}) => ({ - onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), - ...rest, - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [onClickCb], - ); - - const getDropzoneProps = useMemo( - () => - ({ onClick = () => {}, onDrop = () => {}, onDragOver = () => {}, ...rest } = {}) => ({ - onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), - onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), - // onDragEnter: composeDragHandler(composeEventHandlers(onDragEnter, onDragEnterCb)), - onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)), - ...rest, - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [onClickCb, onDropCb, onDragOverCb], - ); + const onInputElementClick = useCallback((event) => { + event.stopPropagation(); + }, []); - const setProgressBarPercentage = (percentage: number) => { - dispatch({ - progressBarPercentage: percentage, - type: 'setProgressBarPercentage', - }); - }; - - const setDisplayProgressBar = (display: string) => { - dispatch({ - displayProgressBar: display, - type: 'setDisplayProgressBar', - }); - }; + // ===================== return ( <> - {childrenIsFunction() ? ( - // button - <>{renderChildren()} - ) : ( - // drop div - //
-
- {props.children} -
- )} + {renderChildren() || ''} ); }; @@ -545,58 +425,13 @@ function useCSVReaderComponent(api: Api) { export function useCSVReader() { const [config, setConfig] = useState({}); - const [accept, setAccept] = useState(DEFAULT_ACCEPT); - const [minSize, setMinSize] = useState(0); - const [maxSize, setMaxSize] = useState(3000000); - const [maxFiles, setMaxFiles] = useState(1); - const [className, setClassName] = useState(''); - const [style, setStyle] = useState({}); - const [removeButtonColor, setRemoveButtonColor] = useState(''); - const [validator, setValidator] = useState(null); - const [noClick, setNoClick] = useState(false); const [noDrag, setNoDrag] = useState(false); - const [noRemoveButton, setNoRemoveButton] = useState(false); - const [isReset, setIsReset] = useState(false); - const [disabled, setDisabled] = useState(false); - const [noKeyboard, setNoKeyboard] = useState(false); - const [noDragEventsBubbling, setNoDragEventsBubbling] = useState(false); - const [multiple, setMultiple] = useState(false); const api = { config, setConfig, - accept, - setAccept, - minSize, - setMinSize, - maxSize, - setMaxSize, - maxFiles, - setMaxFiles, - className, - setClassName, - style, - setStyle, - removeButtonColor, - setRemoveButtonColor, - validator, - setValidator, - noClick, - setNoClick, noDrag, setNoDrag, - noRemoveButton, - setNoRemoveButton, - isReset, - setIsReset, - disabled, - setDisabled, - noKeyboard, - setNoKeyboard, - noDragEventsBubbling, - setNoDragEventsBubbling, - multiple, - setMultiple, } as Api; const CSVReader = useCSVReaderComponent(api); @@ -611,49 +446,42 @@ const initialState = { displayProgressBar: 'none', progressBarPercentage: 0, + isDragActive: false, isFileDialogActive: false, + isFocused: false, + + draggedFiles: [], acceptedFiles: [], - acceptedFile: null, - // isFocused: false, - // isDragActive: false, - // isDragAccept: false, - // isDragReject: false, - // draggedFiles: [], - // fileRejections: [], }; function reducer(state: any, action: any) { switch (action.type) { - case 'openDialog': - return { - ...state, - isFileDialogActive: true, - }; - case 'closeDialog': + case 'focus': return { ...state, - isFileDialogActive: false, + isFocused: true, }; - case 'setFiles': + case 'blur': return { ...state, - acceptedFiles: action.acceptedFiles, - fileRejections: action.fileRejections, + isFocused: false, }; - case 'setFile': + case 'openDialog': return { ...state, - acceptedFile: action.acceptedFile, + isFileDialogActive: true, }; - case 'setProgressBarPercentage': + case 'setDraggedFiles': + /* eslint no-case-declarations: 0 */ + const { isDragActive, draggedFiles } = action; return { ...state, - progressBarPercentage: action.progressBarPercentage, + draggedFiles, + isDragActive, }; - case 'setDisplayProgressBar': + case 'reset': return { - ...state, - displayProgressBar: action.displayProgressBar, + ...initialState, }; default: return state; diff --git a/src/utils.ts b/src/utils.ts index a6a11bb..256b02f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -211,3 +211,39 @@ export const TOO_MANY_FILES_REJECTION = { code: TOO_MANY_FILES, message: 'Too many files', }; + +// allow the entire document to be a drag target +export function onDocumentDragOver(event: any) { + event.preventDefault(); +} + +interface Params { + files?: any; + accept?: any; + minSize?: number; + maxSize?: number; + multiple?: any; + maxFiles?: any; +} + +export function allFilesAccepted({ + files, + accept, + minSize, + maxSize, + multiple, + maxFiles, +}: Params) { + if ( + (!multiple && files.length > 1) || + (multiple && maxFiles >= 1 && files.length > maxFiles) + ) { + return false; + } + + return files.every((file: any) => { + const [accepted] = fileAccepted(file, accept); + const [sizeMatch] = fileMatchSize(file, minSize, maxSize); + return accepted && sizeMatch; + }); +} From 4e56862eafa861dbf71d978abc5cdc5bcc89fa2a Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 19 Dec 2021 16:16:08 +0700 Subject: [PATCH 062/140] Add Drop onKeyDown --- src/useCSVReader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 8f2b9c6..f1e75d9 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -284,7 +284,7 @@ function useCSVReaderComponent(api: Api) { composeEventHandlers(onDragLeave, onDragLeaveCb), ), onKeyDown: composeKeyboardHandler( - composeEventHandlers(onKeyDown, onKeyDownCb), + composeEventHandlers(onKeyDown, onKeyDownCb), // Done ), onFocus: composeKeyboardHandler( composeEventHandlers(onFocus, onFocusCb), // Done From 7335f16e7246387dc82b55efc00f1eb068393227 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 19 Dec 2021 16:17:21 +0700 Subject: [PATCH 063/140] Add Drop onDragLeave --- src/useCSVReader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index f1e75d9..5c7806f 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -281,7 +281,7 @@ function useCSVReaderComponent(api: Api) { composeEventHandlers(onDragOver, onDragOverCb), ), onDragLeave: composeDragHandler( - composeEventHandlers(onDragLeave, onDragLeaveCb), + composeEventHandlers(onDragLeave, onDragLeaveCb), // Done ), onKeyDown: composeKeyboardHandler( composeEventHandlers(onKeyDown, onKeyDownCb), // Done From db72aaed82fb2c4593aea6ffe16935e554c1b194 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 19 Dec 2021 18:06:10 +0700 Subject: [PATCH 064/140] Add Drop onDragOver --- src/useCSVReader.tsx | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 5c7806f..d428595 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -29,6 +29,7 @@ export interface Props { children: (fn: any) => void | ReactNode; onUploadAccepted?: (data: ParseResult, file?: any, event?: any) => void; onDragLeave?: (event?: any) => void; + onDragOver?: (event?: any) => void; disabled?: boolean; noClick?: boolean; noDrag?: boolean; @@ -63,6 +64,7 @@ function useCSVReaderComponent(api: Api) { preventDropOnDocument = true, noKeyboard = false, onDragLeave, + onDragOver, } = props; const inputRef: any = useRef(null); @@ -147,22 +149,24 @@ function useCSVReaderComponent(api: Api) { ]); const onDragOverCb = useCallback( - (event) => { + (event: any) => { allowDrop(event); - // const hasFiles = isEventWithFiles(event); - // if (hasFiles && event.dataTransfer) { - // try { - // event.dataTransfer.dropEffect = 'copy' - // } catch {} /* eslint-disable-line no-empty */ - // } + const hasFiles = isEventWithFiles(event); + if (hasFiles && event.dataTransfer) { + try { + event.dataTransfer.dropEffect = 'copy'; + } catch {} /* eslint-disable-line no-empty */ + } + + if (hasFiles && onDragOver) { + onDragOver(event); + } - // if (hasFiles && onDragOver) { - // onDragOver(event) - // } + return false; }, // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [onDragOver, noDragEventsBubbling], ); const onDragEnterCb = useCallback( @@ -278,7 +282,7 @@ function useCSVReaderComponent(api: Api) { composeEventHandlers(onDragEnter, onDragEnterCb), ), onDragOver: composeDragHandler( - composeEventHandlers(onDragOver, onDragOverCb), + composeEventHandlers(onDragOver, onDragOverCb), // Done ), onDragLeave: composeDragHandler( composeEventHandlers(onDragLeave, onDragLeaveCb), // Done From b4116501a9642c8658de9832028c308b87ae4195 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 19 Dec 2021 18:18:34 +0700 Subject: [PATCH 065/140] Add Drop onDragEnter --- src/useCSVReader.tsx | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index d428595..ac5de23 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -14,6 +14,7 @@ import { composeEventHandlers, onDocumentDragOver, isEventWithFiles, + isPropagationStopped, } from './utils'; import ProgressBar from './ProgressBar'; @@ -30,6 +31,7 @@ export interface Props { onUploadAccepted?: (data: ParseResult, file?: any, event?: any) => void; onDragLeave?: (event?: any) => void; onDragOver?: (event?: any) => void; + onDragEnter?: (event?: any) => void; disabled?: boolean; noClick?: boolean; noDrag?: boolean; @@ -65,6 +67,7 @@ function useCSVReaderComponent(api: Api) { noKeyboard = false, onDragLeave, onDragOver, + onDragEnter, } = props; const inputRef: any = useRef(null); @@ -170,11 +173,29 @@ function useCSVReaderComponent(api: Api) { ); const onDragEnterCb = useCallback( - (event) => { + (event: any) => { allowDrop(event); + + dragTargetsRef.current = [...dragTargetsRef.current, event.target] as never[]; + + if (isEventWithFiles(event)) { + if (isPropagationStopped(event) && !noDragEventsBubbling) { + return; + } + + dispatch({ + draggedFiles, + isDragActive: true, + type: 'setDraggedFiles' + }); + + if (onDragEnter) { + onDragEnter(event); + } + } }, // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [onDragEnter, noDragEventsBubbling] ); const stopPropagation = (event: any) => { @@ -279,7 +300,7 @@ function useCSVReaderComponent(api: Api) { onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), onDragEnter: composeDragHandler( - composeEventHandlers(onDragEnter, onDragEnterCb), + composeEventHandlers(onDragEnter, onDragEnterCb), // Done ), onDragOver: composeDragHandler( composeEventHandlers(onDragOver, onDragOverCb), // Done From 0cc23d764a652b4c6205591c4054629e75e857ca Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 20 Dec 2021 00:34:12 +0700 Subject: [PATCH 066/140] Fix getting files --- src/useCSVReader.tsx | 241 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 228 insertions(+), 13 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index ac5de23..1b03f76 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -8,13 +8,16 @@ import React, { useRef, useEffect, } from 'react'; -import { ParseResult } from 'papaparse'; +import PapaParse, { ParseResult } from 'papaparse'; import { CustomConfig } from './model'; import { composeEventHandlers, onDocumentDragOver, isEventWithFiles, isPropagationStopped, + fileAccepted, + fileMatchSize, + TOO_MANY_FILES_REJECTION, } from './utils'; import ProgressBar from './ProgressBar'; @@ -28,16 +31,23 @@ import ProgressBar from './ProgressBar'; export interface Props { children: (fn: any) => void | ReactNode; + config?: CustomConfig; onUploadAccepted?: (data: ParseResult, file?: any, event?: any) => void; onDragLeave?: (event?: any) => void; onDragOver?: (event?: any) => void; onDragEnter?: (event?: any) => void; + validator?: (file: any) => void; + onDropAccepted?: (data: ParseResult, file?: any) => void; disabled?: boolean; noClick?: boolean; noDrag?: boolean; noDragEventsBubbling?: boolean; preventDropOnDocument?: boolean; noKeyboard?: boolean; + multiple?: boolean; + minSize?: number; + maxSize?: number; + maxFiles?: number; } // interface State { @@ -52,14 +62,35 @@ export interface Props { export interface Api { config?: CustomConfig; + setConfig?: (config: CustomConfig) => void; disabled?: boolean; setDisabled?: () => void; + minSize?: number; + setMinSize?: (minSize: number) => void; + maxSize?: number; + setMaxSize?: (maxSize: number) => void; + maxFiles?: number; + setMaxFiles?: (maxFiles: number) => void; } function useCSVReaderComponent(api: Api) { const CSVReaderComponent = (props: Props) => { // Use variables from api as global - const { accept, disabled, setDisabled, noDrag, setNoDrag } = CSVReader.api; + const { + accept, + disabled, + setDisabled, + noDrag, + setNoDrag, + config, + setConfig, + minSize, + setMinSize, + maxSize, + setMaxSize, + maxFiles, + setMaxFiles, + } = CSVReader.api; const { noDragEventsBubbling = false, @@ -68,6 +99,10 @@ function useCSVReaderComponent(api: Api) { onDragLeave, onDragOver, onDragEnter, + multiple = false, + validator, + onDropAccepted, + onUploadAccepted, } = props; const inputRef: any = useRef(null); @@ -75,8 +110,6 @@ function useCSVReaderComponent(api: Api) { const [state, dispatch] = useReducer(reducer, initialState); - console.log(dispatch); - const { acceptedFile, progressBarPercentage, @@ -87,10 +120,28 @@ function useCSVReaderComponent(api: Api) { // global + const setProgressBarPercentage = (percentage: number) => { + dispatch({ + progressBarPercentage: percentage, + type: 'setProgressBarPercentage', + }); + }; + + const setDisplayProgressBar = (display: string) => { + dispatch({ + displayProgressBar: display, + type: 'setDisplayProgressBar', + }); + }; + useEffect(() => { - const { disabled, noDrag } = props; + const { disabled, noDrag, config, minSize, maxSize, maxFiles } = props; disabled && setDisabled(disabled); noDrag && setNoDrag(noDrag); + config && setConfig(config); + minSize && setMinSize(minSize); + maxSize && setMaxSize(maxSize); + maxFiles && setMaxFiles(maxFiles); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -126,11 +177,158 @@ function useCSVReaderComponent(api: Api) { (event) => { allowDrop(event); - alert('OnDrop'); - console.log(draggedFiles); + setProgressBarPercentage(0); + + dragTargetsRef.current = []; + + if (isEventWithFiles(event)) { + if (isPropagationStopped(event) && !noDragEventsBubbling) { + return; + } + + const acceptedFiles = [] as any; + const fileRejections = [] as any; + const files = + event.target.files || + (event.dataTransfer && event.dataTransfer.files); + Array.from(files).forEach((file) => { + const [accepted, acceptError] = fileAccepted(file, accept); + const [sizeMatch, sizeError] = fileMatchSize( + file, + minSize, + maxSize, + ); + const customErrors = validator ? validator(file) : null; + + if (accepted && sizeMatch && !customErrors) { + acceptedFiles.push(file); + } else { + let errors = [acceptError, sizeError]; + + if (customErrors) { + errors = errors.concat(customErrors); + } + + fileRejections.push({ file, errors: errors.filter((e) => e) }); + } + }); + + if ( + (!multiple && acceptedFiles.length > 1) || + (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) + ) { + // Reject everything and empty accepted files + acceptedFiles.forEach((file: any) => { + fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }); + }); + acceptedFiles.splice(0); + } + + dispatch({ + acceptedFiles, + fileRejections, + type: 'setFiles', + }); + + setDisplayProgressBar('block'); + + // if (onDrop) { + // onDrop(acceptedFiles, fileRejections, event) + // } + + // if (fileRejections.length > 0 && onDropRejected) { + // onDropRejected(fileRejections, event) + // } + + // if (acceptedFiles.length > 0 && onDropAccepted) { + // onDropAccepted(acceptedFiles, event) + // } + + let configs = {} as any; + const data: any = []; + const errors: any = []; + const meta: any = []; + const reader = new window.FileReader(); + let percentage = 0; + + configs = Object.assign({}, config, configs); + acceptedFiles.forEach((file: any) => { + dispatch({ + acceptedFile: file, + type: 'setFile', + }); + + configs = { + complete: + config?.complete || config?.step + ? config.complete + : () => { + const obj = { data, errors, meta }; + if (!onDropAccepted && onUploadAccepted) { + onUploadAccepted(obj, file); + } else if (onDropAccepted && !onUploadAccepted) { + onDropAccepted(obj, file); + } + }, + step: config?.step + ? config.step + : (row: any) => { + data.push(row.data); + if (row.errors.length > 0) { + errors.push(row.errors); + } + if (row.length > 0) { + meta.push(row[0].meta); + } + if (config && config.preview) { + percentage = Math.round( + (data.length / config.preview) * 100, + ); + setProgressBarPercentage(percentage); + if (data.length === config.preview) { + if (!onDropAccepted && onUploadAccepted) { + onUploadAccepted(data, file); + } else if (onDropAccepted && !onUploadAccepted) { + onDropAccepted(data, file); + } + } + } else { + const cursor = row.meta.cursor; + const newPercentage = Math.round( + (cursor / file.size) * 100, + ); + if (newPercentage === percentage) { + return; + } + percentage = newPercentage; + } + setProgressBarPercentage(percentage); + }, + }; + reader.onload = (e: any) => { + PapaParse.parse(e.target.result, configs); + }; + reader.onloadend = () => { + setTimeout(() => { + setDisplayProgressBar('none'); + }, 2000); + }; + reader.readAsText(file, config.encoding || 'utf-8'); + }); + } + dispatch({ type: 'reset' }); }, // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [ + multiple, + accept, + minSize, + maxSize, + maxFiles, + validator, + onUploadAccepted, + onDropAccepted, + ], ); // Cb to open the file dialog when click occurs on the dropzone @@ -175,9 +373,12 @@ function useCSVReaderComponent(api: Api) { const onDragEnterCb = useCallback( (event: any) => { allowDrop(event); - - dragTargetsRef.current = [...dragTargetsRef.current, event.target] as never[]; - + + dragTargetsRef.current = [ + ...dragTargetsRef.current, + event.target, + ] as never[]; + if (isEventWithFiles(event)) { if (isPropagationStopped(event) && !noDragEventsBubbling) { return; @@ -186,7 +387,7 @@ function useCSVReaderComponent(api: Api) { dispatch({ draggedFiles, isDragActive: true, - type: 'setDraggedFiles' + type: 'setDraggedFiles', }); if (onDragEnter) { @@ -195,7 +396,7 @@ function useCSVReaderComponent(api: Api) { } }, // eslint-disable-next-line react-hooks/exhaustive-deps - [onDragEnter, noDragEventsBubbling] + [onDragEnter, noDragEventsBubbling], ); const stopPropagation = (event: any) => { @@ -451,12 +652,21 @@ function useCSVReaderComponent(api: Api) { export function useCSVReader() { const [config, setConfig] = useState({}); const [noDrag, setNoDrag] = useState(false); + const [minSize, setMinSize] = useState(0); + const [maxSize, setMaxSize] = useState(3000000); + const [maxFiles, setMaxFiles] = useState(1); const api = { config, setConfig, noDrag, setNoDrag, + minSize, + setMinSize, + maxSize, + setMaxSize, + maxFiles, + setMaxFiles, } as Api; const CSVReader = useCSVReaderComponent(api); @@ -508,6 +718,11 @@ function reducer(state: any, action: any) { return { ...initialState, }; + case 'setProgressBarPercentage': + return { + ...state, + progressBarPercentage: action.progressBarPercentage, + }; default: return state; } From 2c5df4b1faf520e8d666ca94e53bafc6ebe83a4d Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 20 Dec 2021 23:38:58 +0700 Subject: [PATCH 067/140] Fix Drop onClick --- src/useCSVReader.tsx | 60 ++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 1b03f76..3ec75e8 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -18,10 +18,11 @@ import { fileAccepted, fileMatchSize, TOO_MANY_FILES_REJECTION, + isIeOrEdge, } from './utils'; import ProgressBar from './ProgressBar'; -// const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; +const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; // const cssProperty = { // inputFile: { @@ -31,6 +32,7 @@ import ProgressBar from './ProgressBar'; export interface Props { children: (fn: any) => void | ReactNode; + accept?: string; config?: CustomConfig; onUploadAccepted?: (data: ParseResult, file?: any, event?: any) => void; onDragLeave?: (event?: any) => void; @@ -77,7 +79,6 @@ function useCSVReaderComponent(api: Api) { const CSVReaderComponent = (props: Props) => { // Use variables from api as global const { - accept, disabled, setDisabled, noDrag, @@ -90,9 +91,12 @@ function useCSVReaderComponent(api: Api) { setMaxSize, maxFiles, setMaxFiles, + noClick, + setNoClick, } = CSVReader.api; const { + accept = DEFAULT_ACCEPT, noDragEventsBubbling = false, preventDropOnDocument = true, noKeyboard = false, @@ -118,6 +122,18 @@ function useCSVReaderComponent(api: Api) { draggedFiles, } = state; + useEffect(() => { + const { disabled, noDrag, config, minSize, maxSize, maxFiles, noClick } = props; + disabled && setDisabled(disabled); + noDrag && setNoDrag(noDrag); + config && setConfig(config); + minSize && setMinSize(minSize); + maxSize && setMaxSize(maxSize); + maxFiles && setMaxFiles(maxFiles); + noClick && setNoClick(noClick); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // global const setProgressBarPercentage = (percentage: number) => { @@ -134,17 +150,6 @@ function useCSVReaderComponent(api: Api) { }); }; - useEffect(() => { - const { disabled, noDrag, config, minSize, maxSize, maxFiles } = props; - disabled && setDisabled(disabled); - noDrag && setNoDrag(noDrag); - config && setConfig(config); - minSize && setMinSize(minSize); - maxSize && setMaxSize(maxSize); - maxFiles && setMaxFiles(maxFiles); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const renderChildren = () => { const { children, onUploadAccepted } = props; @@ -333,21 +338,19 @@ function useCSVReaderComponent(api: Api) { // Cb to open the file dialog when click occurs on the dropzone const onClickCb = useCallback(() => { - // if (noClick) { - // return; - // } + if (noClick) { + return; + } + // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() // to ensure React can handle state changes - // if (isIeOrEdge()) { - // setTimeout(openFileDialog, 0); - // } else { - // openFileDialog(); - // } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - inputRef, - // noClick, - ]); + if (isIeOrEdge()) { + setTimeout(openFileDialog, 0); + } else { + openFileDialog(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inputRef, noClick]); const onDragOverCb = useCallback( (event: any) => { @@ -498,7 +501,7 @@ function useCSVReaderComponent(api: Api) { refKey = rootRef, ...rest } = {}) => ({ - onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), + onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), // Done onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), onDragEnter: composeDragHandler( composeEventHandlers(onDragEnter, onDragEnterCb), // Done @@ -655,6 +658,7 @@ export function useCSVReader() { const [minSize, setMinSize] = useState(0); const [maxSize, setMaxSize] = useState(3000000); const [maxFiles, setMaxFiles] = useState(1); + const [noClick, setNoClick] = useState(false); const api = { config, @@ -667,6 +671,8 @@ export function useCSVReader() { setMaxSize, maxFiles, setMaxFiles, + noClick, + setNoClick, } as Api; const CSVReader = useCSVReaderComponent(api); From 46a396a1093205fbfbff74c98931880e3718282a Mon Sep 17 00:00:00 2001 From: Bunlong Date: Tue, 21 Dec 2021 00:04:34 +0700 Subject: [PATCH 068/140] Uncomment drop example --- supports/create-next-app/pages/index.tsx | 43 +++++++++++++----------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 08c67bd..ed15728 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -175,7 +175,7 @@ export default function Home() { */}
- {/* { console.log('9999999999999999999999'); console.log(results); @@ -219,7 +219,7 @@ export default function Home() {
)} -
*/} + { @@ -228,24 +228,27 @@ export default function Home() { console.log('9999999999999999999999'); }} > - {({ getDropzoneProps, acceptedFile }) => ( -
- {acceptedFile && acceptedFile.name} -
+ {({ getDropzoneProps, acceptedFile, ProgressBar }) => ( + <> +
+ {acceptedFile && acceptedFile.name} +
+ + )}
From 2779d36bd738ef11ce055ee1c9e93bc65efce0e7 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 23 Dec 2021 01:06:25 +0700 Subject: [PATCH 069/140] Fix displaying progressbar --- src/ProgressBar.tsx | 18 ++++++++---------- src/useCSVReader.tsx | 29 ++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/ProgressBar.tsx b/src/ProgressBar.tsx index ec4c377..1d13f05 100644 --- a/src/ProgressBar.tsx +++ b/src/ProgressBar.tsx @@ -31,7 +31,7 @@ interface Props { } export default function ProgressBar(props: Props) { - const { style, className, display, isButton } = props; + const { style, className, display } = props; const [percentage, setPercentage] = useState(0); useEffect(() => { @@ -39,14 +39,12 @@ export default function ProgressBar(props: Props) { }, [props.percentage]); return ( -
- -
+ ); } diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 3ec75e8..746bb1b 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -116,14 +116,14 @@ function useCSVReaderComponent(api: Api) { const { acceptedFile, + draggedFiles, progressBarPercentage, displayProgressBar, - - draggedFiles, } = state; useEffect(() => { - const { disabled, noDrag, config, minSize, maxSize, maxFiles, noClick } = props; + const { disabled, noDrag, config, minSize, maxSize, maxFiles, noClick } = + props; disabled && setDisabled(disabled); noDrag && setNoDrag(noDrag); config && setConfig(config); @@ -169,7 +169,6 @@ function useCSVReaderComponent(api: Api) { const ProgressBarComponent = (props: any) => { return ( (api: Api) { type: 'setFiles', }); - setDisplayProgressBar('block'); + // setDisplayProgressBar('block'); // if (onDrop) { // onDrop(acceptedFiles, fileRejections, event) @@ -289,7 +288,6 @@ function useCSVReaderComponent(api: Api) { percentage = Math.round( (data.length / config.preview) * 100, ); - setProgressBarPercentage(percentage); if (data.length === config.preview) { if (!onDropAccepted && onUploadAccepted) { onUploadAccepted(data, file); @@ -308,9 +306,11 @@ function useCSVReaderComponent(api: Api) { percentage = newPercentage; } setProgressBarPercentage(percentage); + // setDisplayProgressBar('block'); }, }; reader.onload = (e: any) => { + setDisplayProgressBar('block'); PapaParse.parse(e.target.result, configs); }; reader.onloadend = () => { @@ -349,7 +349,7 @@ function useCSVReaderComponent(api: Api) { } else { openFileDialog(); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [inputRef, noClick]); const onDragOverCb = useCallback( @@ -417,12 +417,13 @@ function useCSVReaderComponent(api: Api) { // Fn for opening the file dialog programmatically const openFileDialog = useCallback(() => { - // if (inputRef.current && state.displayProgressBarStatus) { - if (inputRef.current) { + if (inputRef.current && state.displayProgressBar) { + // if (inputRef.current) { dispatch({ type: 'openDialog' }); inputRef.current.value = null; inputRef.current.click(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [dispatch]); // ===================== @@ -633,6 +634,11 @@ function useCSVReaderComponent(api: Api) { // ===================== + console.log('99999999999999'); + console.log(displayProgressBar); + console.log(progressBarPercentage); + console.log('99999999999999'); + return ( <> @@ -729,6 +735,11 @@ function reducer(state: any, action: any) { ...state, progressBarPercentage: action.progressBarPercentage, }; + case 'setDisplayProgressBar': + return { + ...state, + displayProgressBar: action.displayProgressBar, + }; default: return state; } From 6f0d62c2fa6e41450f739b47e0d3dd966601bf87 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 24 Dec 2021 00:05:33 +0700 Subject: [PATCH 070/140] Add onClick to button --- src/useCSVReader.tsx | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 746bb1b..9d0a720 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -152,7 +152,9 @@ function useCSVReaderComponent(api: Api) { const renderChildren = () => { const { children, onUploadAccepted } = props; - + console.log('999999999999999999999999'); + console.log(state.acceptedFile); + console.log('999999999999999999999999'); return onUploadAccepted ? children({ getButtonProps, @@ -580,14 +582,12 @@ function useCSVReaderComponent(api: Api) { // Button const getButtonProps = useMemo( () => - ({ /*onClick = () => {},*/ ...rest } = {}) => ({ - // onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), + ({ onClick = () => {}, ...rest } = {}) => ({ + onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), ...rest, }), // eslint-disable-next-line react-hooks/exhaustive-deps - [ - /*onClickCb*/ - ], + [onClickCb], ); // ===================== @@ -634,11 +634,6 @@ function useCSVReaderComponent(api: Api) { // ===================== - console.log('99999999999999'); - console.log(displayProgressBar); - console.log(progressBarPercentage); - console.log('99999999999999'); - return ( <> @@ -699,6 +694,11 @@ const initialState = { draggedFiles: [], acceptedFiles: [], + acceptedFile: null, + + // isDragAccept: false, + // isDragReject: false, + // fileRejections: [], }; function reducer(state: any, action: any) { @@ -740,6 +740,17 @@ function reducer(state: any, action: any) { ...state, displayProgressBar: action.displayProgressBar, }; + case 'setFiles': + return { + ...state, + acceptedFiles: action.acceptedFiles, + fileRejections: action.fileRejections, + }; + case 'setFile': + return { + ...state, + acceptedFile: action.acceptedFile, + }; default: return state; } From 46173e4328fee576649f753004e5bbb9ac2b6c2a Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sun, 26 Dec 2021 23:57:25 +0700 Subject: [PATCH 071/140] Fix performance --- src/useCSVReader.tsx | 577 +++++++++-------------- supports/create-next-app/pages/index.tsx | 8 +- 2 files changed, 215 insertions(+), 370 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 9d0a720..6312891 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -1,24 +1,24 @@ +/* eslint-disable react-hooks/exhaustive-deps */ import React, { // CSSProperties, useReducer, useCallback, useMemo, + useEffect, useState, ReactNode, useRef, - useEffect, } from 'react'; import PapaParse, { ParseResult } from 'papaparse'; import { CustomConfig } from './model'; import { composeEventHandlers, - onDocumentDragOver, + isIeOrEdge, isEventWithFiles, isPropagationStopped, fileAccepted, fileMatchSize, TOO_MANY_FILES_REJECTION, - isIeOrEdge, } from './utils'; import ProgressBar from './ProgressBar'; @@ -34,113 +34,140 @@ export interface Props { children: (fn: any) => void | ReactNode; accept?: string; config?: CustomConfig; + disabled?: boolean; + minSize?: number; + maxSize?: number; + maxFiles?: number; + onUploadAccepted?: (data: ParseResult, file?: any, event?: any) => void; - onDragLeave?: (event?: any) => void; - onDragOver?: (event?: any) => void; - onDragEnter?: (event?: any) => void; - validator?: (file: any) => void; onDropAccepted?: (data: ParseResult, file?: any) => void; - disabled?: boolean; + noClick?: boolean; noDrag?: boolean; noDragEventsBubbling?: boolean; - preventDropOnDocument?: boolean; - noKeyboard?: boolean; multiple?: boolean; - minSize?: number; - maxSize?: number; - maxFiles?: number; -} -// interface State { -// dropAreaCustom: any; -// progressBar: number; -// displayProgressBarStatus: string; -// file: any; -// files: any; -// removeIconColor: string; -// isCanceled: boolean; -// } + validator?: (file: any) => void; +} export interface Api { + accept?: string; + setAccept?: () => void; config?: CustomConfig; - setConfig?: (config: CustomConfig) => void; + setConfig?: () => void; disabled?: boolean; setDisabled?: () => void; minSize?: number; - setMinSize?: (minSize: number) => void; + setMinSize?: () => void; maxSize?: number; - setMaxSize?: (maxSize: number) => void; + setMaxSize?: () => void; maxFiles?: number; - setMaxFiles?: (maxFiles: number) => void; + setMaxFiles?: () => void; + + noClick?: boolean; + setNoClick?: () => void; + noDrag?: boolean; + setNoDrag?: () => void; + multiple?: boolean; + setMultiple?: () => void; } function useCSVReaderComponent(api: Api) { const CSVReaderComponent = (props: Props) => { - // Use variables from api as global + const inputRef: any = useRef(null); + // const rootRef: any = useRef(null); + const dragTargetsRef = useRef([]); + const { - disabled, - setDisabled, - noDrag, - setNoDrag, + accept, + setAccept, config, setConfig, + disabled, + setDisabled, minSize, setMinSize, maxSize, setMaxSize, maxFiles, setMaxFiles, + noClick, setNoClick, + noDrag, + setNoDrag, + multiple, + setMultiple, } = CSVReader.api; const { - accept = DEFAULT_ACCEPT, - noDragEventsBubbling = false, - preventDropOnDocument = true, - noKeyboard = false, - onDragLeave, - onDragOver, - onDragEnter, - multiple = false, - validator, + children, onDropAccepted, onUploadAccepted, + noDragEventsBubbling, + validator, } = props; - const inputRef: any = useRef(null); - const rootRef: any = useRef(null); - const [state, dispatch] = useReducer(reducer, initialState); - - const { - acceptedFile, - draggedFiles, - progressBarPercentage, - displayProgressBar, - } = state; + const { acceptedFile, displayProgressBar, progressBarPercentage } = state; useEffect(() => { - const { disabled, noDrag, config, minSize, maxSize, maxFiles, noClick } = - props; - disabled && setDisabled(disabled); - noDrag && setNoDrag(noDrag); + const { + accept, + config, + disabled, + minSize, + maxSize, + maxFiles, + + noClick, + multiple, + } = props; + + accept && setAccept(accept); config && setConfig(config); + disabled && setDisabled(disabled); minSize && setMinSize(minSize); maxSize && setMaxSize(maxSize); maxFiles && setMaxFiles(maxFiles); + noClick && setNoClick(noClick); - // eslint-disable-next-line react-hooks/exhaustive-deps + noDrag && setNoDrag(noDrag); + multiple && setMultiple(multiple); }, []); - // global + const ProgressBarComponent = (props: any) => { + return ( + + ); + }; + + // ============== GLOBAL ============== + const composeHandler = (fn: any) => { + return disabled ? null : fn; + }; - const setProgressBarPercentage = (percentage: number) => { - dispatch({ - progressBarPercentage: percentage, - type: 'setProgressBarPercentage', - }); + const composeDragHandler = (fn: any) => { + return noDrag ? null : composeHandler(fn); + }; + + const stopPropagation = (event: any) => { + if (noDragEventsBubbling) { + event.stopPropagation(); + } + }; + + const allowDrop = (event: any) => { + event.preventDefault(event); + // Persist here because we need the event later after getFilesFromEvent() is done + event.persist(); + stopPropagation(event); }; const setDisplayProgressBar = (display: string) => { @@ -150,11 +177,14 @@ function useCSVReaderComponent(api: Api) { }); }; + const setProgressBarPercentage = (percentage: number) => { + dispatch({ + progressBarPercentage: percentage, + type: 'setProgressBarPercentage', + }); + }; + const renderChildren = () => { - const { children, onUploadAccepted } = props; - console.log('999999999999999999999999'); - console.log(state.acceptedFile); - console.log('999999999999999999999999'); return onUploadAccepted ? children({ getButtonProps, @@ -168,16 +198,32 @@ function useCSVReaderComponent(api: Api) { }); }; - const ProgressBarComponent = (props: any) => { - return ( - - ); - }; + // Fn for opening the file dialog programmatically + const openFileDialog = useCallback(() => { + if (inputRef.current && state.displayProgressBar) { + // if (inputRef.current) { + dispatch({ type: 'openDialog' }); + inputRef.current.value = null; + inputRef.current.click(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [dispatch]); + + // Cb to open the file dialog when click occurs on the dropzone + const onClickCb = useCallback(() => { + if (noClick) { + return; + } + + // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() + // to ensure React can handle state changes + if (isIeOrEdge()) { + setTimeout(openFileDialog, 0); + } else { + openFileDialog(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inputRef, noClick]); const onDropCb = useCallback( (event) => { @@ -236,7 +282,7 @@ function useCSVReaderComponent(api: Api) { type: 'setFiles', }); - // setDisplayProgressBar('block'); + setDisplayProgressBar('block'); // if (onDrop) { // onDrop(acceptedFiles, fileRejections, event) @@ -290,6 +336,7 @@ function useCSVReaderComponent(api: Api) { percentage = Math.round( (data.length / config.preview) * 100, ); + // setProgressBarPercentage(percentage); if (data.length === config.preview) { if (!onDropAccepted && onUploadAccepted) { onUploadAccepted(data, file); @@ -308,11 +355,9 @@ function useCSVReaderComponent(api: Api) { percentage = newPercentage; } setProgressBarPercentage(percentage); - // setDisplayProgressBar('block'); }, }; reader.onload = (e: any) => { - setDisplayProgressBar('block'); PapaParse.parse(e.target.result, configs); }; reader.onloadend = () => { @@ -325,7 +370,6 @@ function useCSVReaderComponent(api: Api) { } dispatch({ type: 'reset' }); }, - // eslint-disable-next-line react-hooks/exhaustive-deps [ multiple, accept, @@ -337,268 +381,83 @@ function useCSVReaderComponent(api: Api) { onDropAccepted, ], ); + // ==================================== - // Cb to open the file dialog when click occurs on the dropzone - const onClickCb = useCallback(() => { - if (noClick) { - return; - } - - // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() - // to ensure React can handle state changes - if (isIeOrEdge()) { - setTimeout(openFileDialog, 0); - } else { - openFileDialog(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [inputRef, noClick]); - - const onDragOverCb = useCallback( - (event: any) => { - allowDrop(event); - - const hasFiles = isEventWithFiles(event); - if (hasFiles && event.dataTransfer) { - try { - event.dataTransfer.dropEffect = 'copy'; - } catch {} /* eslint-disable-line no-empty */ - } - - if (hasFiles && onDragOver) { - onDragOver(event); - } - - return false; - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [onDragOver, noDragEventsBubbling], - ); - - const onDragEnterCb = useCallback( - (event: any) => { - allowDrop(event); - - dragTargetsRef.current = [ - ...dragTargetsRef.current, - event.target, - ] as never[]; - - if (isEventWithFiles(event)) { - if (isPropagationStopped(event) && !noDragEventsBubbling) { - return; - } - - dispatch({ - draggedFiles, - isDragActive: true, - type: 'setDraggedFiles', - }); - - if (onDragEnter) { - onDragEnter(event); - } - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [onDragEnter, noDragEventsBubbling], - ); - - const stopPropagation = (event: any) => { - if (noDragEventsBubbling) { - event.stopPropagation(); - } - }; - - const allowDrop = (event: any) => { - event.preventDefault(event); - // Persist here because we need the event later after getFilesFromEvent() is done - event.persist(); - stopPropagation(event); - }; - - // Fn for opening the file dialog programmatically - const openFileDialog = useCallback(() => { - if (inputRef.current && state.displayProgressBar) { - // if (inputRef.current) { - dispatch({ type: 'openDialog' }); - inputRef.current.value = null; - inputRef.current.click(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dispatch]); - - // ===================== - - // Dropzone - - // Update focus state for the dropzone - const onFocusCb = useCallback(() => { - dispatch({ type: 'focus' }); - }, []); - const onBlurCb = useCallback(() => { - dispatch({ type: 'blur' }); - }, []); - - const onDragLeaveCb = useCallback( - (event: any) => { - allowDrop(event); - - // Only deactivate once the dropzone and all children have been left - const targets = dragTargetsRef.current.filter( - (target) => 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) - const targetIdx = targets.indexOf(event.target as never); - if (targetIdx !== -1) { - targets.splice(targetIdx, 1); - } - dragTargetsRef.current = targets; - if (targets.length > 0) { - return; - } - - dispatch({ - isDragActive: false, - type: 'setDraggedFiles', - draggedFiles: [], - }); - - if (isEventWithFiles(event) && onDragLeave) { - onDragLeave(event); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [rootRef, onDragLeave, noDragEventsBubbling], - ); - - // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone - const onKeyDownCb = useCallback( - (event: any) => { - // Ignore keyboard events bubbling up the DOM tree - if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) { - return; - } - - if (event.keyCode === 32 || event.keyCode === 13) { - event.preventDefault(); - openFileDialog(); - } - }, + // ============== BUTTON ============== + const getButtonProps = useMemo( + () => + ({ onClick = () => {}, ...rest } = {}) => ({ + onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), + ...rest, + }), // eslint-disable-next-line react-hooks/exhaustive-deps - [rootRef, inputRef], + [onClickCb], ); + // ==================================== + // ============== DROP ============== const getDropzoneProps = useMemo( () => ({ onClick = () => {}, - onDragOver = () => {}, onDrop = () => {}, - onDragEnter = () => {}, - onDragLeave = () => {}, - onKeyDown = () => {}, - onFocus = () => {}, - onBlur = () => {}, - refKey = rootRef, + // onDragOver = () => {}, + // onDragEnter = () => {}, + // onDragLeave = () => {}, + // onKeyDown = () => {}, + // onFocus = () => {}, + // onBlur = () => {}, + // refKey = rootRef, ...rest } = {}) => ({ onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), // Done onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), - onDragEnter: composeDragHandler( - composeEventHandlers(onDragEnter, onDragEnterCb), // Done - ), - onDragOver: composeDragHandler( - composeEventHandlers(onDragOver, onDragOverCb), // Done - ), - onDragLeave: composeDragHandler( - composeEventHandlers(onDragLeave, onDragLeaveCb), // Done - ), - onKeyDown: composeKeyboardHandler( - composeEventHandlers(onKeyDown, onKeyDownCb), // Done - ), - onFocus: composeKeyboardHandler( - composeEventHandlers(onFocus, onFocusCb), // Done - ), - onBlur: composeKeyboardHandler( - composeEventHandlers(onBlur, onBlurCb), // Done - ), - [refKey]: rootRef, + // onDragEnter: composeDragHandler( + // composeEventHandlers(onDragEnter, onDragEnterCb), // Done + // ), + // onDragOver: composeDragHandler( + // composeEventHandlers(onDragOver, onDragOverCb), // Done + // ), + // onDragLeave: composeDragHandler( + // composeEventHandlers(onDragLeave, onDragLeaveCb), // Done + // ), + // onKeyDown: composeKeyboardHandler( + // composeEventHandlers(onKeyDown, onKeyDownCb), // Done + // ), + // onFocus: composeKeyboardHandler( + // composeEventHandlers(onFocus, onFocusCb), // Done + // ), + // onBlur: composeKeyboardHandler( + // composeEventHandlers(onBlur, onBlurCb), // Done + // ), + // [refKey]: rootRef, ...rest, }), // eslint-disable-next-line react-hooks/exhaustive-deps [ - rootRef, - onKeyDownCb, - onFocusCb, - onBlurCb, - onClickCb, - onDragEnterCb, - onDragOverCb, - onDragLeaveCb, - onDropCb, - noKeyboard, - noDrag, + // rootRef, + // onKeyDownCb, + // onFocusCb, + // onBlurCb, + // onClickCb, + // onDragEnterCb, + // onDragOverCb, + // onDragLeaveCb, + // onDropCb, + // noKeyboard, + // noDrag, disabled, ], ); + // ================================== - const dragTargetsRef = useRef([]); - const onDocumentDrop = (event: any) => { - 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(() => { - if (preventDropOnDocument) { - document.addEventListener('dragover', onDocumentDragOver, true); - document.addEventListener('drop', onDocumentDrop, true); - } - - return () => { - if (preventDropOnDocument) { - document.removeEventListener('dragover', onDocumentDragOver); - document.removeEventListener('drop', onDocumentDrop); - } - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [rootRef, preventDropOnDocument]); - - const composeDragHandler = (fn: any) => { - return noDrag ? null : composeHandler(fn); - }; - - const composeKeyboardHandler = (fn: any) => { - return noKeyboard ? null : composeHandler(fn); - }; - - // ===================== - - // Button - const getButtonProps = useMemo( - () => - ({ onClick = () => {}, ...rest } = {}) => ({ - onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), - ...rest, - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [onClickCb], - ); - // ===================== - - // ======= Input ======= - + // ============== INPUT ============== const getInputProps = useMemo( () => ({ refKey = 'ref', onChange = () => {}, - onClick = () => {}, + // onClick = () => {}, ...rest } = {}) => { const inputProps = { @@ -607,9 +466,9 @@ function useCSVReaderComponent(api: Api) { type: 'file', style: { display: 'none' }, onChange: composeHandler(composeEventHandlers(onChange, onDropCb)), - onClick: composeHandler( - composeEventHandlers(onClick, onInputElementClick), - ), + // onClick: composeHandler( + // composeEventHandlers(onClick, onInputElementClick), + // ), autoComplete: 'off', tabIndex: -1, [refKey]: inputRef, @@ -621,32 +480,24 @@ function useCSVReaderComponent(api: Api) { }; }, // eslint-disable-next-line react-hooks/exhaustive-deps - [inputRef, accept, onDropCb, disabled], + [ + inputRef, + accept, + // onDropCb, + disabled, + ], ); - - const composeHandler = (fn: any) => { - return disabled ? null : fn; - }; - - const onInputElementClick = useCallback((event) => { - event.stopPropagation(); - }, []); - - // ===================== + // =================================== return ( <> - {renderChildren() || ''} + {renderChildren()} ); }; - const CSVReader = useMemo( - () => CSVReaderComponent, - // eslint-disable-next-line react-hooks/exhaustive-deps - [], - ) as any; + const CSVReader = useMemo(() => CSVReaderComponent, []) as any; CSVReader.api = api; @@ -654,26 +505,37 @@ function useCSVReaderComponent(api: Api) { } export function useCSVReader() { + const [accept, setAccept] = useState(DEFAULT_ACCEPT); const [config, setConfig] = useState({}); - const [noDrag, setNoDrag] = useState(false); + const [disabled, setDisabled] = useState(false); const [minSize, setMinSize] = useState(0); const [maxSize, setMaxSize] = useState(3000000); const [maxFiles, setMaxFiles] = useState(1); + const [noClick, setNoClick] = useState(false); + const [noDrag, setNoDrag] = useState(false); + const [multiple, setMultiple] = useState(false); const api = { + accept, + setAccept, config, setConfig, - noDrag, - setNoDrag, + disabled, + setDisabled, minSize, setMinSize, maxSize, setMaxSize, + multiple, + setMultiple, maxFiles, setMaxFiles, + noClick, setNoClick, + noDrag, + setNoDrag, } as Api; const CSVReader = useCSVReaderComponent(api); @@ -703,53 +565,36 @@ const initialState = { function reducer(state: any, action: any) { switch (action.type) { - case 'focus': - return { - ...state, - isFocused: true, - }; - case 'blur': - return { - ...state, - isFocused: false, - }; case 'openDialog': return { ...state, isFileDialogActive: true, }; - case 'setDraggedFiles': - /* eslint no-case-declarations: 0 */ - const { isDragActive, draggedFiles } = action; + case 'closeDialog': return { ...state, - draggedFiles, - isDragActive, + isFileDialogActive: false, }; - case 'reset': + case 'setFiles': return { - ...initialState, + ...state, + acceptedFiles: action.acceptedFiles, + fileRejections: action.fileRejections, }; - case 'setProgressBarPercentage': + case 'setFile': return { ...state, - progressBarPercentage: action.progressBarPercentage, + acceptedFile: action.acceptedFile, }; case 'setDisplayProgressBar': return { ...state, displayProgressBar: action.displayProgressBar, }; - case 'setFiles': - return { - ...state, - acceptedFiles: action.acceptedFiles, - fileRejections: action.fileRejections, - }; - case 'setFile': + case 'setProgressBarPercentage': return { ...state, - acceptedFile: action.acceptedFile, + progressBarPercentage: action.progressBarPercentage, }; default: return state; diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index ed15728..837fcdf 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -232,7 +232,6 @@ export default function Home() { <>
- {acceptedFile && acceptedFile.name} + + {acceptedFile && acceptedFile.name} + +
- )} From 4e693717e8f037be81a2a78ce829d4b138bf1f0d Mon Sep 17 00:00:00 2001 From: Bunlong Date: Tue, 28 Dec 2021 00:31:00 +0700 Subject: [PATCH 072/140] Fix drop --- src/useCSVReader.tsx | 234 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 188 insertions(+), 46 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 6312891..c7ab0cf 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -45,9 +45,14 @@ export interface Props { noClick?: boolean; noDrag?: boolean; noDragEventsBubbling?: boolean; + noKeyboard?: boolean; multiple?: boolean; validator?: (file: any) => void; + + onDragEnter?: (event?: any) => void; + onDragOver?: (event?: any) => void; + onDragLeave?: (event?: any) => void; } export interface Api { @@ -75,7 +80,7 @@ export interface Api { function useCSVReaderComponent(api: Api) { const CSVReaderComponent = (props: Props) => { const inputRef: any = useRef(null); - // const rootRef: any = useRef(null); + const rootRef: any = useRef(null); const dragTargetsRef = useRef([]); const { @@ -106,10 +111,16 @@ function useCSVReaderComponent(api: Api) { onUploadAccepted, noDragEventsBubbling, validator, + + onDragEnter, + onDragOver, + onDragLeave, + + noKeyboard = false, } = props; const [state, dispatch] = useReducer(reducer, initialState); - const { acceptedFile, displayProgressBar, progressBarPercentage } = state; + const { acceptedFile, displayProgressBar, progressBarPercentage, draggedFiles } = state; useEffect(() => { const { @@ -381,6 +392,10 @@ function useCSVReaderComponent(api: Api) { onDropAccepted, ], ); + + const onInputElementClick = useCallback((event) => { + event.stopPropagation(); + }, []); // ==================================== // ============== BUTTON ============== @@ -396,56 +411,166 @@ function useCSVReaderComponent(api: Api) { // ==================================== // ============== DROP ============== + const composeKeyboardHandler = (fn: any) => { + return noKeyboard ? null : composeHandler(fn); + }; + + const onDragEnterCb = useCallback( + (event: any) => { + allowDrop(event); + + dragTargetsRef.current = [ + ...dragTargetsRef.current, + event.target, + ] as never[]; + + if (isEventWithFiles(event)) { + if (isPropagationStopped(event) && !noDragEventsBubbling) { + return; + } + + dispatch({ + draggedFiles, + isDragActive: true, + type: 'setDraggedFiles', + }); + + if (onDragEnter) { + onDragEnter(event); + } + } + }, + [onDragEnter, noDragEventsBubbling], + ); + + const onDragOverCb = useCallback( + (event: any) => { + allowDrop(event); + + const hasFiles = isEventWithFiles(event); + if (hasFiles && event.dataTransfer) { + try { + event.dataTransfer.dropEffect = 'copy'; + } catch {} /* eslint-disable-line no-empty */ + } + + if (hasFiles && onDragOver) { + onDragOver(event); + } + + return false; + }, + [onDragOver, noDragEventsBubbling], + ); + + const onDragLeaveCb = useCallback( + (event: any) => { + allowDrop(event); + + // Only deactivate once the dropzone and all children have been left + const targets = dragTargetsRef.current.filter( + (target) => 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) + const targetIdx = targets.indexOf(event.target as never); + if (targetIdx !== -1) { + targets.splice(targetIdx, 1); + } + dragTargetsRef.current = targets; + if (targets.length > 0) { + return; + } + + dispatch({ + isDragActive: false, + type: 'setDraggedFiles', + draggedFiles: [], + }); + + if (isEventWithFiles(event) && onDragLeave) { + onDragLeave(event); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [rootRef, onDragLeave, noDragEventsBubbling], + ); + + // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone + const onKeyDownCb = useCallback( + (event: any) => { + // Ignore keyboard events bubbling up the DOM tree + if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) { + return; + } + + if (event.keyCode === 32 || event.keyCode === 13) { + event.preventDefault(); + openFileDialog(); + } + }, + [rootRef, inputRef], + ); + + // Update focus state for the dropzone + const onFocusCb = useCallback(() => { + dispatch({ type: 'focus' }); + }, []); + + const onBlurCb = useCallback(() => { + dispatch({ type: 'blur' }); + }, []); + const getDropzoneProps = useMemo( () => ({ onClick = () => {}, onDrop = () => {}, - // onDragOver = () => {}, + onDragOver = () => {}, + onDragLeave = () => {}, + onKeyDown = () => {}, + onFocus = () => {}, + onBlur = () => {}, // onDragEnter = () => {}, - // onDragLeave = () => {}, - // onKeyDown = () => {}, - // onFocus = () => {}, - // onBlur = () => {}, - // refKey = rootRef, + refKey = rootRef, ...rest } = {}) => ({ - onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), // Done + onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), - // onDragEnter: composeDragHandler( - // composeEventHandlers(onDragEnter, onDragEnterCb), // Done - // ), - // onDragOver: composeDragHandler( - // composeEventHandlers(onDragOver, onDragOverCb), // Done - // ), - // onDragLeave: composeDragHandler( - // composeEventHandlers(onDragLeave, onDragLeaveCb), // Done - // ), - // onKeyDown: composeKeyboardHandler( - // composeEventHandlers(onKeyDown, onKeyDownCb), // Done - // ), - // onFocus: composeKeyboardHandler( - // composeEventHandlers(onFocus, onFocusCb), // Done - // ), - // onBlur: composeKeyboardHandler( - // composeEventHandlers(onBlur, onBlurCb), // Done - // ), - // [refKey]: rootRef, + onDragEnter: composeDragHandler( + composeEventHandlers(onDragEnter, onDragEnterCb), + ), + onDragOver: composeDragHandler( + composeEventHandlers(onDragOver, onDragOverCb), + ), + onDragLeave: composeDragHandler( + composeEventHandlers(onDragLeave, onDragLeaveCb), + ), + onKeyDown: composeKeyboardHandler( + composeEventHandlers(onKeyDown, onKeyDownCb), // Done + ), + onFocus: composeKeyboardHandler( + composeEventHandlers(onFocus, onFocusCb), // Done + ), + onBlur: composeKeyboardHandler( + composeEventHandlers(onBlur, onBlurCb), // Done + ), + [refKey]: rootRef, ...rest, }), // eslint-disable-next-line react-hooks/exhaustive-deps [ - // rootRef, - // onKeyDownCb, - // onFocusCb, - // onBlurCb, - // onClickCb, - // onDragEnterCb, - // onDragOverCb, - // onDragLeaveCb, - // onDropCb, - // noKeyboard, - // noDrag, + rootRef, + onKeyDownCb, + onFocusCb, + onBlurCb, + onClickCb, + onDragEnterCb, + onDragOverCb, + onDragLeaveCb, + onDropCb, + noKeyboard, + noDrag, disabled, ], ); @@ -457,18 +582,18 @@ function useCSVReaderComponent(api: Api) { ({ refKey = 'ref', onChange = () => {}, - // onClick = () => {}, + onClick = () => {}, ...rest } = {}) => { const inputProps = { accept, - // multiple, + multiple, type: 'file', style: { display: 'none' }, onChange: composeHandler(composeEventHandlers(onChange, onDropCb)), - // onClick: composeHandler( - // composeEventHandlers(onClick, onInputElementClick), - // ), + onClick: composeHandler( + composeEventHandlers(onClick, onInputElementClick), + ), autoComplete: 'off', tabIndex: -1, [refKey]: inputRef, @@ -479,11 +604,10 @@ function useCSVReaderComponent(api: Api) { ...rest, }; }, - // eslint-disable-next-line react-hooks/exhaustive-deps [ inputRef, accept, - // onDropCb, + onDropCb, disabled, ], ); @@ -596,6 +720,24 @@ function reducer(state: any, action: any) { ...state, progressBarPercentage: action.progressBarPercentage, }; + case 'setDraggedFiles': + /* eslint no-case-declarations: 0 */ + const { isDragActive, draggedFiles } = action; + return { + ...state, + draggedFiles, + isDragActive, + }; + case 'focus': + return { + ...state, + isFocused: true, + }; + case 'blur': + return { + ...state, + isFocused: false, + }; default: return state; } From 22e2f3b71c6cdba26bc680e038fa382b5587f60e Mon Sep 17 00:00:00 2001 From: Bunlong Date: Tue, 28 Dec 2021 22:58:21 +0700 Subject: [PATCH 073/140] Set Event type --- package.json | 2 +- src/useCSVReader.tsx | 278 +++++++++++++++++++++++-------------------- 2 files changed, 153 insertions(+), 127 deletions(-) diff --git a/package.json b/package.json index 1820be0..b6566a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-papaparse", - "version": "3.18.1", + "version": "4.0.0", "description": "The fastest in-browser CSV (or delimited text) parser for React. It is full of useful features such as CSVReader, CSVDownloader, readString, jsonToCSV, readRemoteFile, ... etc.", "author": "Bunlong ", "license": "MIT", diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index c7ab0cf..ec7545e 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -1,6 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ import React, { - // CSSProperties, useReducer, useCallback, useMemo, @@ -19,40 +18,40 @@ import { fileAccepted, fileMatchSize, TOO_MANY_FILES_REJECTION, + onDocumentDragOver, } from './utils'; import ProgressBar from './ProgressBar'; const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; -// const cssProperty = { -// inputFile: { -// display: 'none', -// } as CSSProperties, -// }; - export interface Props { children: (fn: any) => void | ReactNode; accept?: string; config?: CustomConfig; - disabled?: boolean; + minSize?: number; maxSize?: number; maxFiles?: number; - onUploadAccepted?: (data: ParseResult, file?: any, event?: any) => void; - onDropAccepted?: (data: ParseResult, file?: any) => void; - + disabled?: boolean; noClick?: boolean; noDrag?: boolean; noDragEventsBubbling?: boolean; noKeyboard?: boolean; multiple?: boolean; - - validator?: (file: any) => void; - - onDragEnter?: (event?: any) => void; - onDragOver?: (event?: any) => void; - onDragLeave?: (event?: any) => void; + preventDropOnDocument?: boolean; + + onUploadAccepted?: (data: ParseResult, file?: File, event?: Event) => void; + onDropAccepted?: ( + data: ParseResult, + file?: File, + event?: DragEvent | Event, + ) => void; + onDropRejected?: (file?: File, event?: DragEvent | Event) => void; + validator?: (file: File) => void; + onDragEnter?: (event?: DragEvent) => void; + onDragOver?: (event?: DragEvent) => void; + onDragLeave?: (event?: DragEvent) => void; } export interface Api { @@ -68,7 +67,6 @@ export interface Api { setMaxSize?: () => void; maxFiles?: number; setMaxFiles?: () => void; - noClick?: boolean; setNoClick?: () => void; noDrag?: boolean; @@ -77,6 +75,11 @@ export interface Api { setMultiple?: () => void; } +export interface ProgressBarComponentProp { + style?: any; + className?: string; +} + function useCSVReaderComponent(api: Api) { const CSVReaderComponent = (props: Props) => { const inputRef: any = useRef(null); @@ -96,7 +99,6 @@ function useCSVReaderComponent(api: Api) { setMaxSize, maxFiles, setMaxFiles, - noClick, setNoClick, noDrag, @@ -109,18 +111,23 @@ function useCSVReaderComponent(api: Api) { children, onDropAccepted, onUploadAccepted, + onDropRejected, noDragEventsBubbling, validator, - onDragEnter, onDragOver, onDragLeave, - noKeyboard = false, + preventDropOnDocument = true, } = props; const [state, dispatch] = useReducer(reducer, initialState); - const { acceptedFile, displayProgressBar, progressBarPercentage, draggedFiles } = state; + const { + acceptedFile, + displayProgressBar, + progressBarPercentage, + draggedFiles, + } = state; useEffect(() => { const { @@ -130,7 +137,6 @@ function useCSVReaderComponent(api: Api) { minSize, maxSize, maxFiles, - noClick, multiple, } = props; @@ -141,24 +147,34 @@ function useCSVReaderComponent(api: Api) { minSize && setMinSize(minSize); maxSize && setMaxSize(maxSize); maxFiles && setMaxFiles(maxFiles); - noClick && setNoClick(noClick); noDrag && setNoDrag(noDrag); multiple && setMultiple(multiple); }, []); - const ProgressBarComponent = (props: any) => { - return ( - - ); + const onDocumentDrop = (event: DragEvent) => { + 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(() => { + if (preventDropOnDocument) { + document.addEventListener('dragover', onDocumentDragOver, false); + document.addEventListener('drop', onDocumentDrop, false); + } + + return () => { + if (preventDropOnDocument) { + document.removeEventListener('dragover', onDocumentDragOver); + document.removeEventListener('drop', onDocumentDrop); + } + }; + }, [rootRef, preventDropOnDocument]); + // ============== GLOBAL ============== const composeHandler = (fn: any) => { return disabled ? null : fn; @@ -168,7 +184,7 @@ function useCSVReaderComponent(api: Api) { return noDrag ? null : composeHandler(fn); }; - const stopPropagation = (event: any) => { + const stopPropagation = (event: Event) => { if (noDragEventsBubbling) { event.stopPropagation(); } @@ -195,6 +211,20 @@ function useCSVReaderComponent(api: Api) { }); }; + const ProgressBarComponent = ({ + style, + className, + }: ProgressBarComponentProp) => { + return ( + + ); + }; + const renderChildren = () => { return onUploadAccepted ? children({ @@ -261,7 +291,7 @@ function useCSVReaderComponent(api: Api) { minSize, maxSize, ); - const customErrors = validator ? validator(file) : null; + const customErrors = validator ? validator(file as File) : null; if (accepted && sizeMatch && !customErrors) { acceptedFiles.push(file); @@ -281,7 +311,7 @@ function useCSVReaderComponent(api: Api) { (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) ) { // Reject everything and empty accepted files - acceptedFiles.forEach((file: any) => { + acceptedFiles.forEach((file: File) => { fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }); }); acceptedFiles.splice(0); @@ -299,87 +329,88 @@ function useCSVReaderComponent(api: Api) { // onDrop(acceptedFiles, fileRejections, event) // } - // if (fileRejections.length > 0 && onDropRejected) { - // onDropRejected(fileRejections, event) - // } - - // if (acceptedFiles.length > 0 && onDropAccepted) { - // onDropAccepted(acceptedFiles, event) - // } - - let configs = {} as any; - const data: any = []; - const errors: any = []; - const meta: any = []; - const reader = new window.FileReader(); - let percentage = 0; - - configs = Object.assign({}, config, configs); - acceptedFiles.forEach((file: any) => { - dispatch({ - acceptedFile: file, - type: 'setFile', - }); + if (fileRejections.length > 0 && onDropRejected) { + onDropRejected(fileRejections, event); + } - configs = { - complete: - config?.complete || config?.step - ? config.complete - : () => { - const obj = { data, errors, meta }; - if (!onDropAccepted && onUploadAccepted) { - onUploadAccepted(obj, file); - } else if (onDropAccepted && !onUploadAccepted) { - onDropAccepted(obj, file); - } - }, - step: config?.step - ? config.step - : (row: any) => { - data.push(row.data); - if (row.errors.length > 0) { - errors.push(row.errors); - } - if (row.length > 0) { - meta.push(row[0].meta); - } - if (config && config.preview) { - percentage = Math.round( - (data.length / config.preview) * 100, - ); - // setProgressBarPercentage(percentage); - if (data.length === config.preview) { + if ( + acceptedFiles.length > 0 && + (onUploadAccepted || onDropAccepted) + ) { + let configs = {} as any; + const data: any = []; + const errors: any = []; + const meta: any = []; + const reader = new window.FileReader(); + let percentage = 0; + + configs = Object.assign({}, config, configs); + acceptedFiles.forEach((file: File) => { + dispatch({ + acceptedFile: file, + type: 'setFile', + }); + + configs = { + complete: + config?.complete || config?.step + ? config.complete + : () => { + const obj = { data, errors, meta }; if (!onDropAccepted && onUploadAccepted) { - onUploadAccepted(data, file); + onUploadAccepted(obj, file); } else if (onDropAccepted && !onUploadAccepted) { - onDropAccepted(data, file); + onDropAccepted(obj, file); } + }, + step: config?.step + ? config.step + : (row: any) => { + data.push(row.data); + if (row.errors.length > 0) { + errors.push(row.errors); } - } else { - const cursor = row.meta.cursor; - const newPercentage = Math.round( - (cursor / file.size) * 100, - ); - if (newPercentage === percentage) { - return; + if (row.length > 0) { + meta.push(row[0].meta); } - percentage = newPercentage; - } - setProgressBarPercentage(percentage); - }, - }; - reader.onload = (e: any) => { - PapaParse.parse(e.target.result, configs); - }; - reader.onloadend = () => { - setTimeout(() => { - setDisplayProgressBar('none'); - }, 2000); - }; - reader.readAsText(file, config.encoding || 'utf-8'); - }); + if (config && config.preview) { + percentage = Math.round( + (data.length / config.preview) * 100, + ); + // setProgressBarPercentage(percentage); + if (data.length === config.preview) { + if (!onDropAccepted && onUploadAccepted) { + onUploadAccepted(data, file); + } else if (onDropAccepted && !onUploadAccepted) { + onDropAccepted(data, file); + } + } + } else { + const cursor = row.meta.cursor; + const newPercentage = Math.round( + (cursor / file.size) * 100, + ); + if (newPercentage === percentage) { + return; + } + percentage = newPercentage; + } + setProgressBarPercentage(percentage); + }, + }; + reader.onload = (e: any) => { + PapaParse.parse(e.target.result, configs); + }; + reader.onloadend = () => { + setTimeout(() => { + setDisplayProgressBar('none'); + }, 2000); + }; + reader.readAsText(file, config.encoding || 'utf-8'); + }); + } } - dispatch({ type: 'reset' }); + // dispatch({ type: 'reset' }); }, [ multiple, @@ -405,7 +436,6 @@ function useCSVReaderComponent(api: Api) { onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), ...rest, }), - // eslint-disable-next-line react-hooks/exhaustive-deps [onClickCb], ); // ==================================== @@ -416,7 +446,7 @@ function useCSVReaderComponent(api: Api) { }; const onDragEnterCb = useCallback( - (event: any) => { + (event: DragEvent) => { allowDrop(event); dragTargetsRef.current = [ @@ -444,7 +474,7 @@ function useCSVReaderComponent(api: Api) { ); const onDragOverCb = useCallback( - (event: any) => { + (event: DragEvent) => { allowDrop(event); const hasFiles = isEventWithFiles(event); @@ -464,7 +494,7 @@ function useCSVReaderComponent(api: Api) { ); const onDragLeaveCb = useCallback( - (event: any) => { + (event: DragEvent) => { allowDrop(event); // Only deactivate once the dropzone and all children have been left @@ -492,13 +522,12 @@ function useCSVReaderComponent(api: Api) { onDragLeave(event); } }, - // eslint-disable-next-line react-hooks/exhaustive-deps [rootRef, onDragLeave, noDragEventsBubbling], ); // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone const onKeyDownCb = useCallback( - (event: any) => { + (event: KeyboardEvent) => { // Ignore keyboard events bubbling up the DOM tree if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) { return; @@ -531,7 +560,7 @@ function useCSVReaderComponent(api: Api) { onKeyDown = () => {}, onFocus = () => {}, onBlur = () => {}, - // onDragEnter = () => {}, + onDragEnter = () => {}, refKey = rootRef, ...rest } = {}) => ({ @@ -558,7 +587,6 @@ function useCSVReaderComponent(api: Api) { [refKey]: rootRef, ...rest, }), - // eslint-disable-next-line react-hooks/exhaustive-deps [ rootRef, onKeyDownCb, @@ -604,12 +632,7 @@ function useCSVReaderComponent(api: Api) { ...rest, }; }, - [ - inputRef, - accept, - onDropCb, - disabled, - ], + [inputRef, accept, onDropCb, disabled], ); // =================================== @@ -655,7 +678,6 @@ export function useCSVReader() { setMultiple, maxFiles, setMaxFiles, - noClick, setNoClick, noDrag, @@ -738,6 +760,10 @@ function reducer(state: any, action: any) { ...state, isFocused: false, }; + // case 'reset': + // return { + // ...initialState + // } default: return state; } From ec4cd634583bcb56aec1d21eab7ac634143bd83e Mon Sep 17 00:00:00 2001 From: Bunlong Date: Tue, 28 Dec 2021 23:42:08 +0700 Subject: [PATCH 074/140] Fix type --- src/useCSVReader.tsx | 3 +-- supports/create-next-app/pages/index.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index ec7545e..926f2b6 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -656,9 +656,8 @@ export function useCSVReader() { const [config, setConfig] = useState({}); const [disabled, setDisabled] = useState(false); const [minSize, setMinSize] = useState(0); - const [maxSize, setMaxSize] = useState(3000000); + const [maxSize, setMaxSize] = useState(Infinity); const [maxFiles, setMaxFiles] = useState(1); - const [noClick, setNoClick] = useState(false); const [noDrag, setNoDrag] = useState(false); const [multiple, setMultiple] = useState(false); diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 837fcdf..403cbd8 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -182,7 +182,7 @@ export default function Home() { console.log('9999999999999999999999'); }} > - {({ getButtonProps, acceptedFile, ProgressBar }) => ( + {({ getButtonProps, acceptedFile, ProgressBar }: any) => (
- {({ getDropzoneProps, acceptedFile, ProgressBar }) => ( + {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( <>
- - {acceptedFile && acceptedFile.name} + + {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} - +
)} From 18f3851df634ce05af5472390e95b52d876922cd Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 00:09:09 +0700 Subject: [PATCH 075/140] Add comment --- src/useCSVReader.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 926f2b6..dd1ac1b 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -22,6 +22,9 @@ import { } from './utils'; import ProgressBar from './ProgressBar'; +// 'text/csv' for MacOS +// '.csv' for Linux +// 'application/vnd.ms-excel' for Window 10 const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; export interface Props { From ceb4022155e05675e618c9252062e0af8983aa50 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 15:07:58 +0700 Subject: [PATCH 076/140] Update Basic Upload --- README.md | 115 ++++++----------------- supports/create-next-app/pages/index.tsx | 12 +-- 2 files changed, 33 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index ec4612c..523810a 100644 --- a/README.md +++ b/README.md @@ -66,108 +66,51 @@ FAQ: ![basic-upload](https://react-papaparse.github.io/static/images/csvreader1.png) ```javascript -import React, { Component } from 'react' - -import { CSVReader } from 'react-papaparse' - -const buttonRef = React.createRef() - -export default class CSVReader extends Component { - handleOpenDialog = (e) => { - // Note that the ref is set async, so it might be null at some point - if (buttonRef.current) { - buttonRef.current.open(e) - } - } - - handleOnFileLoad = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - - handleOnError = (err, file, inputElem, reason) => { - console.log(err) - } - - handleOnRemoveFile = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - - handleRemoveFile = (e) => { - // Note that the ref is set async, so it might be null at some point - if (buttonRef.current) { - buttonRef.current.removeFile(e) - } - } - - render() { - return ( - - {({ file }) => ( - - )} - - ) - } +
+ + + )} + + ); } ``` diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 403cbd8..fd442aa 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -177,17 +177,13 @@ export default function Home() {
{ - console.log('9999999999999999999999'); + console.log('---------------------------'); console.log(results); - console.log('9999999999999999999999'); + console.log('---------------------------'); }} > {({ getButtonProps, acceptedFile, ProgressBar }: any) => ( -
+ <>
-
+ )}
From 4f7d6934a5b95d7a625ed7f409c07c22e9a49686 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 15:32:42 +0700 Subject: [PATCH 077/140] Update Click and Drag Upload --- README.md | 67 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 523810a..a814fda 100644 --- a/README.md +++ b/README.md @@ -119,40 +119,43 @@ export default function CSVReader() { ![click-and-drag-upload](https://react-papaparse.github.io/static/images/csvreader2.png) ```javascript -import React, { Component } from 'react' - -import { CSVReader } from 'react-papaparse' - -export default class CSVReader extends Component { - handleOnDrop = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - - handleOnError = (err, file, inputElem, reason) => { - console.log(err) - } +import { useCSVReader } from 'react-papaparse'; - handleOnRemoveFile = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +export default function CSVReader() { + const { CSVReader } = useCSVReader(); - render() { - return ( - - Drop CSV file here or click to upload. - - ) - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }} + > + {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( + <> +
+ + {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} + + +
+ + )} +
+ ); } ``` From ecdde1c1857ad2fe83ac2797f6df0beb3212da6a Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 15:42:40 +0700 Subject: [PATCH 078/140] Update Drag ( No Click ) Upload --- README.md | 70 +++++++++++++----------- supports/create-next-app/pages/index.tsx | 7 ++- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index a814fda..c62b49e 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ export default function CSVReader() { {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( <>
{acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} @@ -164,40 +164,44 @@ export default function CSVReader() { ![drag-no-click-upload](https://react-papaparse.github.io/static/images/csvreader3.png) ```javascript -import React, { Component } from 'react' - -import { CSVReader } from 'react-papaparse' - -export default class CSVReader extends Component { - handleOnDrop = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - - handleOnError = (err, file, inputElem, reason) => { - console.log(err) - } +import { useCSVReader } from 'react-papaparse'; - handleOnRemoveFile = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +export default function CSVReader() { + const { CSVReader } = useCSVReader(); - render() { - return ( - - Drop CSV file here to upload. - - ) - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }} + noClick + > + {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( + <> +
+ + {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} + + +
+ + )} +
+ ); } ``` diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index fd442aa..c7de748 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -219,14 +219,16 @@ export default function Home() { { - console.log('9999999999999999999999'); + console.log('---------------------------'); console.log(results); - console.log('9999999999999999999999'); + console.log('---------------------------'); }} + noClick > {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( <>
{acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} From 9c2255181d19818ebe9ab2ce26a6a04670b39082 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 17:22:34 +0700 Subject: [PATCH 079/140] Update No Drag --- README.md | 68 +++++++++++++----------- src/useCSVReader.tsx | 1 + supports/create-next-app/pages/index.tsx | 2 +- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index c62b49e..eaa99ac 100644 --- a/README.md +++ b/README.md @@ -210,40 +210,44 @@ export default function CSVReader() { ![click-no-drag-upload](https://react-papaparse.github.io/static/images/csvreader4.png) ```javascript -import React, { Component } from 'react' - -import { CSVReader } from 'react-papaparse' - -export default class CSVReader extends Component { - handleOnDrop = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - - handleOnError = (err, file, inputElem, reason) => { - console.log(err) - } +import { useCSVReader } from 'react-papaparse'; - handleOnRemoveFile = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +export default function CSVReader() { + const { CSVReader } = useCSVReader(); - render() { - return ( - - Click to upload. - - ) - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }} + noDrag + > + {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( + <> +
+ + {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} + + +
+ + )} +
+ ); } ``` diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index dd1ac1b..ce89be4 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -141,6 +141,7 @@ function useCSVReaderComponent(api: Api) { maxSize, maxFiles, noClick, + noDrag, multiple, } = props; diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index c7de748..5e156d7 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -223,7 +223,7 @@ export default function Home() { console.log(results); console.log('---------------------------'); }} - noClick + noDrag > {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( <> From c8db4b872fd694f53d1b8f0e8cb7e3dc6b245aac Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 22:18:49 +0700 Subject: [PATCH 080/140] Update CSVDownloader button --- README.md | 82 ++++++++++++++++++++++---------------------- src/useCSVReader.tsx | 2 -- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index eaa99ac..38b2fad 100644 --- a/README.md +++ b/README.md @@ -260,48 +260,48 @@ Just pass in the js object with an optional [configuration](https://react-papapa #### Button ```javascript -import React, { Component } from 'react' - -import { CSVDownloader } from 'react-papaparse' +import { useDownloader } from 'react-papaparse' -export default class CSVDownloader extends Component { - render() { - return ( - - Download - - ) - } +export default function CSVDownloader() { + return ( + + Download + + ); } ``` diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index ce89be4..9b02e8e 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -251,7 +251,6 @@ function useCSVReaderComponent(api: Api) { inputRef.current.value = null; inputRef.current.click(); } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [dispatch]); // Cb to open the file dialog when click occurs on the dropzone @@ -267,7 +266,6 @@ function useCSVReaderComponent(api: Api) { } else { openFileDialog(); } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [inputRef, noClick]); const onDropCb = useCallback( From 35891072005a976a1dacf1fe17400f1ea602e758 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 22:31:16 +0700 Subject: [PATCH 081/140] Update CSVDownloader link --- README.md | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 38b2fad..35b2a40 100644 --- a/README.md +++ b/README.md @@ -308,26 +308,22 @@ export default function CSVDownloader() { #### Link ```javascript -import React, { Component } from 'react' - -import { CSVDownloader } from 'react-papaparse' +import { useDownloader } from 'react-papaparse' -export default class CSVDownloader extends Component { - render() { - return ( - - Download - - ) - } + filename={'filename'} + type={Type.Link} + > + Download + + ); } ``` From 0804e497352738ae4cc3c000dfae0149eaf2c8e4 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 23:18:34 +0700 Subject: [PATCH 082/140] Update Data as a Function/Callback --- README.md | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 35b2a40..bf8cd6c 100644 --- a/README.md +++ b/README.md @@ -332,21 +332,28 @@ export default function CSVDownloader() { `data={}` can be a function that returns a data object. ```javascript - { - return [ - { - "Column 1": "1-1", - "Column 2": "1-2", - "Column 3": "1-3", - "Column 4": "1-4", +import { useDownloader } from 'react-papaparse' + +export default function CSVDownloader() { + return ( + { + return [ + { + "Column 1": "1-1", + "Column 2": "1-2", + "Column 3": "1-3", + "Column 4": "1-4", + } + ]} } - ]} - } -> - Download - + filename={'filename'} + type={Type.Link} + > + Download + + ); +} ``` ### 🎀 readString From 3531d504ffcfdb9fff7452fd74467bc7aae0dd20 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 29 Dec 2021 23:23:33 +0700 Subject: [PATCH 083/140] Update import useCSVDownloader --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bf8cd6c..5fe231b 100644 --- a/README.md +++ b/README.md @@ -260,9 +260,11 @@ Just pass in the js object with an optional [configuration](https://react-papapa #### Button ```javascript -import { useDownloader } from 'react-papaparse' +import { useCSVDownloader } from 'react-papaparse' export default function CSVDownloader() { + const { CSVDownloader, Type } = useCSVDownloader(); + return ( { From 75f66050b7287c4a885eda16708d205d3a990cfb Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 30 Dec 2021 22:35:41 +0700 Subject: [PATCH 084/140] Update readString --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5fe231b..d22efc6 100644 --- a/README.md +++ b/README.md @@ -365,20 +365,24 @@ export default function CSVDownloader() { ### 🎀 readString ```javascript -import { readString } from 'react-papaparse' +import { usePapaParse } from 'react-papaparse'; + +const { readString } = usePapaParse(); const csvString = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 2-1,2-2,2-3,2-4 3-1,3-2,3-3,3-4 -4,5,6,7` +4,5,6,7`; readString(csvString, { worker: true, complete: (results) => { - console.log(results) - } -}) + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }, +}); ``` ### 🎀 readRemoteFile From 92fb556bf2ecb50bcb8fd4ac2eb5302cc0fdfb1f Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 30 Dec 2021 22:45:59 +0700 Subject: [PATCH 085/140] Update readRemoteFile --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d22efc6..c44786c 100644 --- a/README.md +++ b/README.md @@ -388,7 +388,9 @@ readString(csvString, { ### 🎀 readRemoteFile ```javascript -import { readRemoteFile } from 'react-papaparse' +import { usePapaParse } from 'react-papaparse'; + +const { readRemoteFile } = usePapaParse(); readRemoteFile( url, From 62f4bac1a8e5ce9ba02462dc1d4dcb8d3bc7bdc2 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 30 Dec 2021 23:02:46 +0700 Subject: [PATCH 086/140] Update jsonToCSV --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c44786c..2d61869 100644 --- a/README.md +++ b/README.md @@ -399,13 +399,15 @@ readRemoteFile( console.log('Results:', results) } } -) +); ``` ### 🎀 jsonToCSV ```javascript -import { jsonToCSV } from 'react-papaparse' +import { usePapaParse } from 'react-papaparse'; + +const { jsonToCSV } = usePapaParse(); const jsonData = `[ { @@ -434,7 +436,7 @@ const jsonData = `[ } ]` -const results = jsonToCSV(jsonData) +const results = jsonToCSV(jsonData); ``` #### Header row support From 9683313322fc5ef6978ee761df52eea2760383da Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 30 Dec 2021 23:09:26 +0700 Subject: [PATCH 087/140] Update Stream --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2d61869..c804e6c 100644 --- a/README.md +++ b/README.md @@ -396,7 +396,9 @@ readRemoteFile( url, { complete: (results) => { + console.log('---------------------------'); console.log('Results:', results) + console.log('---------------------------'); } } ); @@ -448,9 +450,11 @@ readString(csvString, { header: true, worker: true, complete: (results) => { - console.log(results) - } -}) + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }, +}); ``` #### Stream @@ -465,7 +469,7 @@ readRemoteFile('http://example.com/big.csv', { complete: () => { console.log('All done!') } -}) +}); ``` ## 📜 Changelog From c6e3502024bdf70ba017b90aa776247ead9b4843 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 31 Dec 2021 09:23:26 +0700 Subject: [PATCH 088/140] Update Changelog --- README.md | 9 +- supports/create-next-app/pages/index.tsx | 326 +++++++++-------------- 2 files changed, 135 insertions(+), 200 deletions(-) diff --git a/README.md b/README.md index c804e6c..296e7c8 100644 --- a/README.md +++ b/README.md @@ -476,16 +476,15 @@ readRemoteFile('http://example.com/big.csv', { Latest version 3.18.1 (2021-11-07): - * Fix a bug when component is unmounted immediately after file load in CSVReader + * Improve code performance + * Rewrite any existing based components to hooks Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/react-papaparse/blob/master/CHANGELOG.md). ## 🛣️ Roadmap -### 🆕 v4.0.x - - * Improve code performance - * Rewrite any existing based components to hooks +### 🆕 v4.1.0 + * CSVReader multiple files drag and drop ## ❗ Issues diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 5e156d7..71879ad 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,30 +1,16 @@ import React, { useState } from 'react' import { - // CSVReader, - // CSVDownloader, - // readString, usePapaParse, useCSVDownloader, useCSVReader, } from 'react-papaparse' export default function Home() { - const [isReset, setIsReset] = useState(false); const { CSVDownloader, Type } = useCSVDownloader(); const { CSVReader } = useCSVReader(); const { readString } = usePapaParse(); - const handleReset = () => { - setIsReset(!isReset) - } - - const handleOnDrop = (data: any) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - const handleOnError = ( err: any, // file, @@ -34,13 +20,7 @@ export default function Home() { console.log(err) } - const handleOnRemoveFile = (data: any) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - - const handleClick = () => { + const handleReadString = () => { const csvString = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 2-1,2-2,2-3,2-4 @@ -60,194 +40,150 @@ export default function Home() { return ( <>
- {/* - Click to upload. - */} -
-
- {/* - */} - {/* - Download - */} - {/* - Download - */} - {/* { - return [ +

+ +

+

+ - Download - */} -

-
- { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - }} - > - {({ getButtonProps, acceptedFile, ProgressBar }: any) => ( - <> -
- + +
+ {acceptedFile && acceptedFile.name} +
+
+ + + )} +
+

+

+ { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }} + noDrag + > + {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( + <>

- {acceptedFile && acceptedFile.name} + + {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} + +
-
- - - )} - - - { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - }} - noDrag - > - {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( - <> -
- - {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} - - -
- - )} -
+ + )} + +

) From 7b41a2bbaa8b765c41532fadb2886e7c9857f08f Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 31 Dec 2021 16:31:44 +0700 Subject: [PATCH 089/140] Refactor useCSVDownloader --- README.md | 2 +- src/useCSVDownloader.tsx | 94 ++++-------------------- supports/create-next-app/pages/index.tsx | 23 +++--- 3 files changed, 28 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 296e7c8..9411ffa 100644 --- a/README.md +++ b/README.md @@ -474,7 +474,7 @@ readRemoteFile('http://example.com/big.csv', { ## 📜 Changelog -Latest version 3.18.1 (2021-11-07): +Latest version 4.0.0 (2022-01-01): * Improve code performance * Rewrite any existing based components to hooks diff --git a/src/useCSVDownloader.tsx b/src/useCSVDownloader.tsx index 2f6197f..976309d 100644 --- a/src/useCSVDownloader.tsx +++ b/src/useCSVDownloader.tsx @@ -17,61 +17,24 @@ export interface Props { config?: UnparseConfig; } -export interface Api { - data: any; - setData?: () => void; - filename: string; - setFilename?: () => void; - type: string; - setType: () => void; - style?: any; - setStyle?: () => void; - className?: any; - setClassName?: () => void; - bom?: boolean; - setBom?: () => void; - config?: UnparseConfig; - setConfig?: () => void; -} - -function useCSVDownloaderComponent(api: Api) { - const CSVDownloaderComponent = (props: Props) => { - const { - setData, - data, - setFilename, - filename, - setType, - type, - setStyle, - style, - className, - setClassName, - bom, - setBom, - config, - setConfig, - } = CSVDownloader.api; - - React.useEffect(() => { - const { data, filename, type, style, className, bom, config } = props; - setData(data); - setFilename(filename); - type && setType(type); - style && setStyle(style); - className && setClassName(className); - bom && setBom(bom); - config && setConfig(config); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - +function useCSVDownloaderComponent() { + const CSVDownloaderComponent = ({ + children, + data, + filename, + type, + style, + className, + bom, + config, + }: Props) => { const download = () => { const bomCode = bom ? '\ufeff' : ''; let csvContent = null; let csvURL = null; if (typeof data === 'function') { - setData(data()); + data = data(); } if (typeof data === 'object') { @@ -106,11 +69,11 @@ function useCSVDownloaderComponent(api: Api) { style={style} className={className} > - {props.children} + {children} ) : ( download()} style={style} className={className}> - {props.children} + {children} )} @@ -123,40 +86,13 @@ function useCSVDownloaderComponent(api: Api) { [], ) as any; - CSVDownloader.api = api; - return CSVDownloader; } export function useCSVDownloader() { - const [data, setData] = React.useState({}); - const [filename, setFilename] = React.useState({}); - const [type, setType] = React.useState(Type.Link); - const [style, setStyle] = React.useState({}); - const [className, setClassName] = React.useState(''); - const [bom, setBom] = React.useState(false); - const [config, setConfig] = React.useState({}); - const api = { - data, - setData, - filename, - setFilename, - type, - setType, - style, - setStyle, - className, - setClassName, - bom, - setBom, - config, - setConfig, - } as Api; - - const CSVDownloader = useCSVDownloaderComponent(api); + const CSVDownloader = useCSVDownloaderComponent(); return { - ...api, CSVDownloader, Type, }; diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 71879ad..65bfc12 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -40,10 +40,10 @@ export default function Home() { return ( <>
-

+

-

-

+

+
- Download + Download ( Button ) - Download + Download ( Link ) - Download + Download ( Data as a Function/Callback ) -

-

+

+
{ console.log('---------------------------'); @@ -150,8 +151,8 @@ export default function Home() { )} -

-

+

+
{ console.log('---------------------------'); @@ -183,7 +184,7 @@ export default function Home() { )} -

+
) From 9042a73204a2328615d8f4473a69552d4498e57c Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 31 Dec 2021 16:57:11 +0700 Subject: [PATCH 090/140] Refactor useCSVDownloader --- src/useCSVDownloader.tsx | 12 +- supports/create-next-app/pages/index.tsx | 260 +++++++++++------------ 2 files changed, 135 insertions(+), 137 deletions(-) diff --git a/src/useCSVDownloader.tsx b/src/useCSVDownloader.tsx index 976309d..ead7fc6 100644 --- a/src/useCSVDownloader.tsx +++ b/src/useCSVDownloader.tsx @@ -20,13 +20,13 @@ export interface Props { function useCSVDownloaderComponent() { const CSVDownloaderComponent = ({ children, - data, + data = {}, filename, - type, - style, - className, - bom, - config, + type = Type.Link, + style = {}, + className = '', + bom = false, + config = {}, }: Props) => { const download = () => { const bomCode = bom ? '\ufeff' : ''; diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 65bfc12..44f851c 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -40,151 +40,149 @@ export default function Home() { return ( <>
-
- -
-
- + Download ( Button ) + + + Download ( Link ) + + { + return [ { "Column 1": "1-1", "Column 2": "1-2", "Column 3": "1-3", "Column 4": "1-4", - }, - { - "Column 1": "2-1", - "Column 2": "2-2", - "Column 3": "2-3", - "Column 4": "2-4", - }, - { - "Column 1": "3-1", - "Column 2": "3-2", - "Column 3": "3-3", - "Column 4": "3-4", - }, - { - "Column 1": 4, - "Column 2": 5, - "Column 3": 6, - "Column 4": 7, - }, - ]} - filename={'filename'} - config={ - { - delimiter: ';', } - } - type={Type.Button} - > - Download ( Button ) - - - Download ( Link ) - - { - return [ - { - "Column 1": "1-1", - "Column 2": "1-2", - "Column 3": "1-3", - "Column 4": "1-4", - } - ]} - } - type={Type.Button} - > - Download ( Data as a Function/Callback ) - -
-
- { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - }} - > - {({ getButtonProps, acceptedFile, ProgressBar }: any) => ( - <> -
+ Download ( Data as a Function/Callback ) + +
+
+ +
+
+ { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }} + > + {({ getButtonProps, acceptedFile, ProgressBar }: any) => ( + <> +
+ -
- {acceptedFile && acceptedFile.name} -
-
- - - )} -
-
-
- { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - }} - noDrag - > - {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( - <> + Browse file +
- - {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} - - + {acceptedFile && acceptedFile.name}
- - )} -
-
+
+ + + )} + +
+
+ { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }} + noDrag + > + {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( + <> +
+ + {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} + + +
+ + )} +
) From bdd3cad90535f491005af5fb25d99d970b5fc142 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 31 Dec 2021 18:14:40 +0700 Subject: [PATCH 091/140] Refactor --- src/CSVReader.tsx | 683 +++++++++++++++++++++++ supports/create-next-app/pages/index.tsx | 8 +- 2 files changed, 687 insertions(+), 4 deletions(-) diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index 45b9c1a..06d1711 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -586,3 +586,686 @@ export default class CSVReader extends React.Component< ); } } + +// 999999999999999999999999999999999999 + +/* eslint-disable react-hooks/exhaustive-deps */ +// import React, { +// useReducer, +// useCallback, +// useMemo, +// useEffect, +// ReactNode, +// useRef, +// } from 'react'; +// import PapaParse, { ParseResult } from 'papaparse'; +// import { CustomConfig } from './model'; +// import { +// composeEventHandlers, +// isIeOrEdge, +// isEventWithFiles, +// isPropagationStopped, +// fileAccepted, +// fileMatchSize, +// TOO_MANY_FILES_REJECTION, +// onDocumentDragOver, +// } from './utils'; +// import ProgressBar from './ProgressBar'; + +// // 'text/csv' for MacOS +// // '.csv' for Linux +// // 'application/vnd.ms-excel' for Window 10 +// const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; + +// export interface Props { +// children: (fn: any) => void | ReactNode; +// accept?: string; +// config?: CustomConfig; + +// minSize?: number; +// maxSize?: number; +// maxFiles?: number; + +// disabled?: boolean; +// noClick?: boolean; +// noDrag?: boolean; +// noDragEventsBubbling?: boolean; +// noKeyboard?: boolean; +// multiple?: boolean; +// preventDropOnDocument?: boolean; + +// onUploadAccepted?: (data: ParseResult, file?: File, event?: Event) => void; +// onDropAccepted?: ( +// data: ParseResult, +// file?: File, +// event?: DragEvent | Event, +// ) => void; +// onDropRejected?: (file?: File, event?: DragEvent | Event) => void; +// validator?: (file: File) => void; +// onDragEnter?: (event?: DragEvent) => void; +// onDragOver?: (event?: DragEvent) => void; +// onDragLeave?: (event?: DragEvent) => void; +// } + +// export interface ProgressBarComponentProp { +// style?: any; +// className?: string; +// } + +// function useCSVReaderComponent() { +// const CSVReaderComponent = ({ +// children, +// accept = DEFAULT_ACCEPT, +// config = {}, +// minSize = 0, +// maxSize = Infinity, +// maxFiles = 1, +// disabled = false, +// noClick = false, +// noDrag = false, +// noDragEventsBubbling = false, +// noKeyboard = false, +// multiple = false, +// preventDropOnDocument = false, +// onUploadAccepted, +// validator, +// onDropRejected, +// onDropAccepted, +// onDragEnter, +// onDragOver, +// onDragLeave, +// }: Props) => { +// const inputRef: any = useRef(null); +// const rootRef: any = useRef(null); +// const dragTargetsRef = useRef([]); + +// const [state, dispatch] = useReducer(reducer, initialState); +// const { +// acceptedFile, +// displayProgressBar, +// progressBarPercentage, +// draggedFiles, +// } = state; + +// const onDocumentDrop = (event: DragEvent) => { +// 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(() => { +// if (preventDropOnDocument) { +// document.addEventListener('dragover', onDocumentDragOver, false); +// document.addEventListener('drop', onDocumentDrop, false); +// } + +// return () => { +// if (preventDropOnDocument) { +// document.removeEventListener('dragover', onDocumentDragOver); +// document.removeEventListener('drop', onDocumentDrop); +// } +// }; +// }, [rootRef, preventDropOnDocument]); + +// // ============== GLOBAL ============== +// const composeHandler = (fn: any) => { +// return disabled ? null : fn; +// }; + +// const composeDragHandler = (fn: any) => { +// return noDrag ? null : composeHandler(fn); +// }; + +// const stopPropagation = (event: Event) => { +// if (noDragEventsBubbling) { +// event.stopPropagation(); +// } +// }; + +// const allowDrop = (event: any) => { +// event.preventDefault(event); +// // Persist here because we need the event later after getFilesFromEvent() is done +// event.persist(); +// stopPropagation(event); +// }; + +// const setDisplayProgressBar = (display: string) => { +// dispatch({ +// displayProgressBar: display, +// type: 'setDisplayProgressBar', +// }); +// }; + +// const setProgressBarPercentage = (percentage: number) => { +// dispatch({ +// progressBarPercentage: percentage, +// type: 'setProgressBarPercentage', +// }); +// }; + +// const ProgressBarComponent = ({ +// style, +// className, +// }: ProgressBarComponentProp) => { +// return ( +// +// ); +// }; + +// const renderChildren = () => { +// return onUploadAccepted +// ? children({ +// getButtonProps, +// acceptedFile, +// ProgressBar: ProgressBarComponent, +// }) +// : children({ +// getDropzoneProps, +// acceptedFile, +// ProgressBar: ProgressBarComponent, +// }); +// }; + +// // Fn for opening the file dialog programmatically +// const openFileDialog = useCallback(() => { +// if (inputRef.current && state.displayProgressBar) { +// // if (inputRef.current) { +// dispatch({ type: 'openDialog' }); +// inputRef.current.value = null; +// inputRef.current.click(); +// } +// }, [dispatch]); + +// // Cb to open the file dialog when click occurs on the dropzone +// const onClickCb = useCallback(() => { +// if (noClick) { +// return; +// } + +// // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() +// // to ensure React can handle state changes +// if (isIeOrEdge()) { +// setTimeout(openFileDialog, 0); +// } else { +// openFileDialog(); +// } +// }, [inputRef, noClick]); + +// const onDropCb = useCallback( +// (event) => { +// allowDrop(event); + +// setProgressBarPercentage(0); + +// dragTargetsRef.current = []; + +// if (isEventWithFiles(event)) { +// if (isPropagationStopped(event) && !noDragEventsBubbling) { +// return; +// } + +// const acceptedFiles = [] as any; +// const fileRejections = [] as any; +// const files = +// event.target.files || +// (event.dataTransfer && event.dataTransfer.files); +// Array.from(files).forEach((file) => { +// const [accepted, acceptError] = fileAccepted(file, accept); +// const [sizeMatch, sizeError] = fileMatchSize( +// file, +// minSize, +// maxSize, +// ); +// const customErrors = validator ? validator(file as File) : null; + +// if (accepted && sizeMatch && !customErrors) { +// acceptedFiles.push(file); +// } else { +// let errors = [acceptError, sizeError]; + +// if (customErrors) { +// errors = errors.concat(customErrors); +// } + +// fileRejections.push({ file, errors: errors.filter((e) => e) }); +// } +// }); + +// if ( +// (!multiple && acceptedFiles.length > 1) || +// (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) +// ) { +// // Reject everything and empty accepted files +// acceptedFiles.forEach((file: File) => { +// fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }); +// }); +// acceptedFiles.splice(0); +// } + +// dispatch({ +// acceptedFiles, +// fileRejections, +// type: 'setFiles', +// }); + +// setDisplayProgressBar('block'); + +// // if (onDrop) { +// // onDrop(acceptedFiles, fileRejections, event) +// // } + +// if (fileRejections.length > 0 && onDropRejected) { +// onDropRejected(fileRejections, event); +// } + +// if ( +// acceptedFiles.length > 0 && +// (onUploadAccepted || onDropAccepted) +// ) { +// let configs = {} as any; +// const data: any = []; +// const errors: any = []; +// const meta: any = []; +// const reader = new window.FileReader(); +// let percentage = 0; + +// configs = Object.assign({}, config, configs); +// acceptedFiles.forEach((file: File) => { +// dispatch({ +// acceptedFile: file, +// type: 'setFile', +// }); + +// configs = { +// complete: +// config?.complete || config?.step +// ? config.complete +// : () => { +// const obj = { data, errors, meta }; +// if (!onDropAccepted && onUploadAccepted) { +// onUploadAccepted(obj, file); +// } else if (onDropAccepted && !onUploadAccepted) { +// onDropAccepted(obj, file); +// } +// }, +// step: config?.step +// ? config.step +// : (row: any) => { +// data.push(row.data); +// if (row.errors.length > 0) { +// errors.push(row.errors); +// } +// if (row.length > 0) { +// meta.push(row[0].meta); +// } +// if (config && config.preview) { +// percentage = Math.round( +// (data.length / config.preview) * 100, +// ); +// // setProgressBarPercentage(percentage); +// if (data.length === config.preview) { +// if (!onDropAccepted && onUploadAccepted) { +// onUploadAccepted(data, file); +// } else if (onDropAccepted && !onUploadAccepted) { +// onDropAccepted(data, file); +// } +// } +// } else { +// const cursor = row.meta.cursor; +// const newPercentage = Math.round( +// (cursor / file.size) * 100, +// ); +// if (newPercentage === percentage) { +// return; +// } +// percentage = newPercentage; +// } +// setProgressBarPercentage(percentage); +// }, +// }; +// reader.onload = (e: any) => { +// PapaParse.parse(e.target.result, configs); +// }; +// reader.onloadend = () => { +// setTimeout(() => { +// setDisplayProgressBar('none'); +// }, 2000); +// }; +// reader.readAsText(file, config.encoding || 'utf-8'); +// }); +// } +// } +// // dispatch({ type: 'reset' }); +// }, +// [ +// multiple, +// accept, +// minSize, +// maxSize, +// maxFiles, +// validator, +// onUploadAccepted, +// onDropAccepted, +// ], +// ); + +// const onInputElementClick = useCallback((event) => { +// stopPropagation(event); +// }, []); +// // ==================================== + +// // ============== BUTTON ============== +// const getButtonProps = useMemo( +// () => +// ({ onClick = () => {}, ...rest } = {}) => ({ +// onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), +// ...rest, +// }), +// [onClickCb], +// ); +// // ==================================== + +// // ============== DROP ============== +// const composeKeyboardHandler = (fn: any) => { +// return noKeyboard ? null : composeHandler(fn); +// }; + +// const onDragEnterCb = useCallback( +// (event: DragEvent) => { +// allowDrop(event); + +// dragTargetsRef.current = [ +// ...dragTargetsRef.current, +// event.target, +// ] as never[]; + +// if (isEventWithFiles(event)) { +// if (isPropagationStopped(event) && !noDragEventsBubbling) { +// return; +// } + +// dispatch({ +// draggedFiles, +// isDragActive: true, +// type: 'setDraggedFiles', +// }); + +// if (onDragEnter) { +// onDragEnter(event); +// } +// } +// }, +// [onDragEnter, noDragEventsBubbling], +// ); + +// const onDragOverCb = useCallback( +// (event: DragEvent) => { +// allowDrop(event); + +// const hasFiles = isEventWithFiles(event); +// if (hasFiles && event.dataTransfer) { +// try { +// event.dataTransfer.dropEffect = 'copy'; +// } catch {} /* eslint-disable-line no-empty */ +// } + +// if (hasFiles && onDragOver) { +// onDragOver(event); +// } + +// return false; +// }, +// [onDragOver, noDragEventsBubbling], +// ); + +// const onDragLeaveCb = useCallback( +// (event: DragEvent) => { +// allowDrop(event); + +// // Only deactivate once the dropzone and all children have been left +// const targets = dragTargetsRef.current.filter( +// (target) => 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) +// const targetIdx = targets.indexOf(event.target as never); +// if (targetIdx !== -1) { +// targets.splice(targetIdx, 1); +// } +// dragTargetsRef.current = targets; +// if (targets.length > 0) { +// return; +// } + +// dispatch({ +// isDragActive: false, +// type: 'setDraggedFiles', +// draggedFiles: [], +// }); + +// if (isEventWithFiles(event) && onDragLeave) { +// onDragLeave(event); +// } +// }, +// [rootRef, onDragLeave, noDragEventsBubbling], +// ); + +// // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone +// const onKeyDownCb = useCallback( +// (event: KeyboardEvent) => { +// // Ignore keyboard events bubbling up the DOM tree +// if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) { +// return; +// } + +// if (event.key === 'Space' || event.key === 'Enter') { +// event.preventDefault(); +// openFileDialog(); +// } +// }, +// [rootRef, inputRef], +// ); + +// // Update focus state for the dropzone +// const onFocusCb = useCallback(() => { +// dispatch({ type: 'focus' }); +// }, []); + +// const onBlurCb = useCallback(() => { +// dispatch({ type: 'blur' }); +// }, []); + +// const getDropzoneProps = useMemo( +// () => +// ({ +// onClick = () => {}, +// onDrop = () => {}, +// onDragOver = () => {}, +// onDragLeave = () => {}, +// onKeyDown = () => {}, +// onFocus = () => {}, +// onBlur = () => {}, +// onDragEnter = () => {}, +// refKey = rootRef, +// ...rest +// } = {}) => ({ +// onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), +// onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), +// onDragEnter: composeDragHandler( +// composeEventHandlers(onDragEnter, onDragEnterCb), +// ), +// onDragOver: composeDragHandler( +// composeEventHandlers(onDragOver, onDragOverCb), +// ), +// onDragLeave: composeDragHandler( +// composeEventHandlers(onDragLeave, onDragLeaveCb), +// ), +// onKeyDown: composeKeyboardHandler( +// composeEventHandlers(onKeyDown, onKeyDownCb), // Done +// ), +// onFocus: composeKeyboardHandler( +// composeEventHandlers(onFocus, onFocusCb), // Done +// ), +// onBlur: composeKeyboardHandler( +// composeEventHandlers(onBlur, onBlurCb), // Done +// ), +// [refKey]: rootRef, +// ...rest, +// }), +// [ +// rootRef, +// onKeyDownCb, +// onFocusCb, +// onBlurCb, +// onClickCb, +// onDragEnterCb, +// onDragOverCb, +// onDragLeaveCb, +// onDropCb, +// noKeyboard, +// noDrag, +// disabled, +// ], +// ); +// // ================================== + +// // ============== INPUT ============== +// const getInputProps = useMemo( +// () => +// ({ +// refKey = 'ref', +// onChange = () => {}, +// onClick = () => {}, +// ...rest +// } = {}) => { +// const inputProps = { +// accept, +// multiple, +// type: 'file', +// style: { display: 'none' }, +// onChange: composeHandler(composeEventHandlers(onChange, onDropCb)), +// onClick: composeHandler( +// composeEventHandlers(onClick, onInputElementClick), +// ), +// autoComplete: 'off', +// tabIndex: -1, +// [refKey]: inputRef, +// }; + +// return { +// ...inputProps, +// ...rest, +// }; +// }, +// [inputRef, accept, onDropCb, disabled], +// ); +// // =================================== + +// return ( +// <> +// +// {renderChildren()} +// +// ); +// }; + +// const CSVReader = useMemo(() => CSVReaderComponent, []) as any; + +// return CSVReader; +// } + +// export function useCSVReader() { +// const CSVReader = useCSVReaderComponent(); + +// return { +// CSVReader, +// }; +// } + +// const initialState = { +// displayProgressBar: 'none', +// progressBarPercentage: 0, + +// isDragActive: false, +// isFileDialogActive: false, +// isFocused: false, + +// draggedFiles: [], +// acceptedFiles: [], +// acceptedFile: null, + +// // isDragAccept: false, +// // isDragReject: false, +// // fileRejections: [], +// }; + +// function reducer(state: any, action: any) { +// switch (action.type) { +// case 'openDialog': +// return { +// ...state, +// isFileDialogActive: true, +// }; +// case 'closeDialog': +// return { +// ...state, +// isFileDialogActive: false, +// }; +// case 'setFiles': +// return { +// ...state, +// acceptedFiles: action.acceptedFiles, +// fileRejections: action.fileRejections, +// }; +// case 'setFile': +// return { +// ...state, +// acceptedFile: action.acceptedFile, +// }; +// case 'setDisplayProgressBar': +// return { +// ...state, +// displayProgressBar: action.displayProgressBar, +// }; +// case 'setProgressBarPercentage': +// return { +// ...state, +// progressBarPercentage: action.progressBarPercentage, +// }; +// case 'setDraggedFiles': +// /* eslint no-case-declarations: 0 */ +// const { isDragActive, draggedFiles } = action; +// return { +// ...state, +// draggedFiles, +// isDragActive, +// }; +// case 'focus': +// return { +// ...state, +// isFocused: true, +// }; +// case 'blur': +// return { +// ...state, +// isFocused: false, +// }; +// // case 'reset': +// // return { +// // ...initialState +// // } +// default: +// return state; +// } +// } + + +// 999999999999999999999999999999999999 diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 44f851c..a8a9750 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -39,7 +39,7 @@ export default function Home() { return ( <> -
+ {/*
)} -
-
+
*/} + {/*
*/} { console.log('---------------------------'); @@ -183,7 +183,7 @@ export default function Home() { )} -
+ {/*
*/} ) } From b6d8a06411c6c3bbf24d08210e043a0cecdbcf98 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Fri, 31 Dec 2021 22:43:21 +0700 Subject: [PATCH 092/140] Refactor --- src/CSVReader.tsx | 683 ------------------------------------------- src/useCSVReader.tsx | 155 ++-------- 2 files changed, 28 insertions(+), 810 deletions(-) diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index 06d1711..45b9c1a 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -586,686 +586,3 @@ export default class CSVReader extends React.Component< ); } } - -// 999999999999999999999999999999999999 - -/* eslint-disable react-hooks/exhaustive-deps */ -// import React, { -// useReducer, -// useCallback, -// useMemo, -// useEffect, -// ReactNode, -// useRef, -// } from 'react'; -// import PapaParse, { ParseResult } from 'papaparse'; -// import { CustomConfig } from './model'; -// import { -// composeEventHandlers, -// isIeOrEdge, -// isEventWithFiles, -// isPropagationStopped, -// fileAccepted, -// fileMatchSize, -// TOO_MANY_FILES_REJECTION, -// onDocumentDragOver, -// } from './utils'; -// import ProgressBar from './ProgressBar'; - -// // 'text/csv' for MacOS -// // '.csv' for Linux -// // 'application/vnd.ms-excel' for Window 10 -// const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; - -// export interface Props { -// children: (fn: any) => void | ReactNode; -// accept?: string; -// config?: CustomConfig; - -// minSize?: number; -// maxSize?: number; -// maxFiles?: number; - -// disabled?: boolean; -// noClick?: boolean; -// noDrag?: boolean; -// noDragEventsBubbling?: boolean; -// noKeyboard?: boolean; -// multiple?: boolean; -// preventDropOnDocument?: boolean; - -// onUploadAccepted?: (data: ParseResult, file?: File, event?: Event) => void; -// onDropAccepted?: ( -// data: ParseResult, -// file?: File, -// event?: DragEvent | Event, -// ) => void; -// onDropRejected?: (file?: File, event?: DragEvent | Event) => void; -// validator?: (file: File) => void; -// onDragEnter?: (event?: DragEvent) => void; -// onDragOver?: (event?: DragEvent) => void; -// onDragLeave?: (event?: DragEvent) => void; -// } - -// export interface ProgressBarComponentProp { -// style?: any; -// className?: string; -// } - -// function useCSVReaderComponent() { -// const CSVReaderComponent = ({ -// children, -// accept = DEFAULT_ACCEPT, -// config = {}, -// minSize = 0, -// maxSize = Infinity, -// maxFiles = 1, -// disabled = false, -// noClick = false, -// noDrag = false, -// noDragEventsBubbling = false, -// noKeyboard = false, -// multiple = false, -// preventDropOnDocument = false, -// onUploadAccepted, -// validator, -// onDropRejected, -// onDropAccepted, -// onDragEnter, -// onDragOver, -// onDragLeave, -// }: Props) => { -// const inputRef: any = useRef(null); -// const rootRef: any = useRef(null); -// const dragTargetsRef = useRef([]); - -// const [state, dispatch] = useReducer(reducer, initialState); -// const { -// acceptedFile, -// displayProgressBar, -// progressBarPercentage, -// draggedFiles, -// } = state; - -// const onDocumentDrop = (event: DragEvent) => { -// 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(() => { -// if (preventDropOnDocument) { -// document.addEventListener('dragover', onDocumentDragOver, false); -// document.addEventListener('drop', onDocumentDrop, false); -// } - -// return () => { -// if (preventDropOnDocument) { -// document.removeEventListener('dragover', onDocumentDragOver); -// document.removeEventListener('drop', onDocumentDrop); -// } -// }; -// }, [rootRef, preventDropOnDocument]); - -// // ============== GLOBAL ============== -// const composeHandler = (fn: any) => { -// return disabled ? null : fn; -// }; - -// const composeDragHandler = (fn: any) => { -// return noDrag ? null : composeHandler(fn); -// }; - -// const stopPropagation = (event: Event) => { -// if (noDragEventsBubbling) { -// event.stopPropagation(); -// } -// }; - -// const allowDrop = (event: any) => { -// event.preventDefault(event); -// // Persist here because we need the event later after getFilesFromEvent() is done -// event.persist(); -// stopPropagation(event); -// }; - -// const setDisplayProgressBar = (display: string) => { -// dispatch({ -// displayProgressBar: display, -// type: 'setDisplayProgressBar', -// }); -// }; - -// const setProgressBarPercentage = (percentage: number) => { -// dispatch({ -// progressBarPercentage: percentage, -// type: 'setProgressBarPercentage', -// }); -// }; - -// const ProgressBarComponent = ({ -// style, -// className, -// }: ProgressBarComponentProp) => { -// return ( -// -// ); -// }; - -// const renderChildren = () => { -// return onUploadAccepted -// ? children({ -// getButtonProps, -// acceptedFile, -// ProgressBar: ProgressBarComponent, -// }) -// : children({ -// getDropzoneProps, -// acceptedFile, -// ProgressBar: ProgressBarComponent, -// }); -// }; - -// // Fn for opening the file dialog programmatically -// const openFileDialog = useCallback(() => { -// if (inputRef.current && state.displayProgressBar) { -// // if (inputRef.current) { -// dispatch({ type: 'openDialog' }); -// inputRef.current.value = null; -// inputRef.current.click(); -// } -// }, [dispatch]); - -// // Cb to open the file dialog when click occurs on the dropzone -// const onClickCb = useCallback(() => { -// if (noClick) { -// return; -// } - -// // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() -// // to ensure React can handle state changes -// if (isIeOrEdge()) { -// setTimeout(openFileDialog, 0); -// } else { -// openFileDialog(); -// } -// }, [inputRef, noClick]); - -// const onDropCb = useCallback( -// (event) => { -// allowDrop(event); - -// setProgressBarPercentage(0); - -// dragTargetsRef.current = []; - -// if (isEventWithFiles(event)) { -// if (isPropagationStopped(event) && !noDragEventsBubbling) { -// return; -// } - -// const acceptedFiles = [] as any; -// const fileRejections = [] as any; -// const files = -// event.target.files || -// (event.dataTransfer && event.dataTransfer.files); -// Array.from(files).forEach((file) => { -// const [accepted, acceptError] = fileAccepted(file, accept); -// const [sizeMatch, sizeError] = fileMatchSize( -// file, -// minSize, -// maxSize, -// ); -// const customErrors = validator ? validator(file as File) : null; - -// if (accepted && sizeMatch && !customErrors) { -// acceptedFiles.push(file); -// } else { -// let errors = [acceptError, sizeError]; - -// if (customErrors) { -// errors = errors.concat(customErrors); -// } - -// fileRejections.push({ file, errors: errors.filter((e) => e) }); -// } -// }); - -// if ( -// (!multiple && acceptedFiles.length > 1) || -// (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) -// ) { -// // Reject everything and empty accepted files -// acceptedFiles.forEach((file: File) => { -// fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }); -// }); -// acceptedFiles.splice(0); -// } - -// dispatch({ -// acceptedFiles, -// fileRejections, -// type: 'setFiles', -// }); - -// setDisplayProgressBar('block'); - -// // if (onDrop) { -// // onDrop(acceptedFiles, fileRejections, event) -// // } - -// if (fileRejections.length > 0 && onDropRejected) { -// onDropRejected(fileRejections, event); -// } - -// if ( -// acceptedFiles.length > 0 && -// (onUploadAccepted || onDropAccepted) -// ) { -// let configs = {} as any; -// const data: any = []; -// const errors: any = []; -// const meta: any = []; -// const reader = new window.FileReader(); -// let percentage = 0; - -// configs = Object.assign({}, config, configs); -// acceptedFiles.forEach((file: File) => { -// dispatch({ -// acceptedFile: file, -// type: 'setFile', -// }); - -// configs = { -// complete: -// config?.complete || config?.step -// ? config.complete -// : () => { -// const obj = { data, errors, meta }; -// if (!onDropAccepted && onUploadAccepted) { -// onUploadAccepted(obj, file); -// } else if (onDropAccepted && !onUploadAccepted) { -// onDropAccepted(obj, file); -// } -// }, -// step: config?.step -// ? config.step -// : (row: any) => { -// data.push(row.data); -// if (row.errors.length > 0) { -// errors.push(row.errors); -// } -// if (row.length > 0) { -// meta.push(row[0].meta); -// } -// if (config && config.preview) { -// percentage = Math.round( -// (data.length / config.preview) * 100, -// ); -// // setProgressBarPercentage(percentage); -// if (data.length === config.preview) { -// if (!onDropAccepted && onUploadAccepted) { -// onUploadAccepted(data, file); -// } else if (onDropAccepted && !onUploadAccepted) { -// onDropAccepted(data, file); -// } -// } -// } else { -// const cursor = row.meta.cursor; -// const newPercentage = Math.round( -// (cursor / file.size) * 100, -// ); -// if (newPercentage === percentage) { -// return; -// } -// percentage = newPercentage; -// } -// setProgressBarPercentage(percentage); -// }, -// }; -// reader.onload = (e: any) => { -// PapaParse.parse(e.target.result, configs); -// }; -// reader.onloadend = () => { -// setTimeout(() => { -// setDisplayProgressBar('none'); -// }, 2000); -// }; -// reader.readAsText(file, config.encoding || 'utf-8'); -// }); -// } -// } -// // dispatch({ type: 'reset' }); -// }, -// [ -// multiple, -// accept, -// minSize, -// maxSize, -// maxFiles, -// validator, -// onUploadAccepted, -// onDropAccepted, -// ], -// ); - -// const onInputElementClick = useCallback((event) => { -// stopPropagation(event); -// }, []); -// // ==================================== - -// // ============== BUTTON ============== -// const getButtonProps = useMemo( -// () => -// ({ onClick = () => {}, ...rest } = {}) => ({ -// onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), -// ...rest, -// }), -// [onClickCb], -// ); -// // ==================================== - -// // ============== DROP ============== -// const composeKeyboardHandler = (fn: any) => { -// return noKeyboard ? null : composeHandler(fn); -// }; - -// const onDragEnterCb = useCallback( -// (event: DragEvent) => { -// allowDrop(event); - -// dragTargetsRef.current = [ -// ...dragTargetsRef.current, -// event.target, -// ] as never[]; - -// if (isEventWithFiles(event)) { -// if (isPropagationStopped(event) && !noDragEventsBubbling) { -// return; -// } - -// dispatch({ -// draggedFiles, -// isDragActive: true, -// type: 'setDraggedFiles', -// }); - -// if (onDragEnter) { -// onDragEnter(event); -// } -// } -// }, -// [onDragEnter, noDragEventsBubbling], -// ); - -// const onDragOverCb = useCallback( -// (event: DragEvent) => { -// allowDrop(event); - -// const hasFiles = isEventWithFiles(event); -// if (hasFiles && event.dataTransfer) { -// try { -// event.dataTransfer.dropEffect = 'copy'; -// } catch {} /* eslint-disable-line no-empty */ -// } - -// if (hasFiles && onDragOver) { -// onDragOver(event); -// } - -// return false; -// }, -// [onDragOver, noDragEventsBubbling], -// ); - -// const onDragLeaveCb = useCallback( -// (event: DragEvent) => { -// allowDrop(event); - -// // Only deactivate once the dropzone and all children have been left -// const targets = dragTargetsRef.current.filter( -// (target) => 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) -// const targetIdx = targets.indexOf(event.target as never); -// if (targetIdx !== -1) { -// targets.splice(targetIdx, 1); -// } -// dragTargetsRef.current = targets; -// if (targets.length > 0) { -// return; -// } - -// dispatch({ -// isDragActive: false, -// type: 'setDraggedFiles', -// draggedFiles: [], -// }); - -// if (isEventWithFiles(event) && onDragLeave) { -// onDragLeave(event); -// } -// }, -// [rootRef, onDragLeave, noDragEventsBubbling], -// ); - -// // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone -// const onKeyDownCb = useCallback( -// (event: KeyboardEvent) => { -// // Ignore keyboard events bubbling up the DOM tree -// if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) { -// return; -// } - -// if (event.key === 'Space' || event.key === 'Enter') { -// event.preventDefault(); -// openFileDialog(); -// } -// }, -// [rootRef, inputRef], -// ); - -// // Update focus state for the dropzone -// const onFocusCb = useCallback(() => { -// dispatch({ type: 'focus' }); -// }, []); - -// const onBlurCb = useCallback(() => { -// dispatch({ type: 'blur' }); -// }, []); - -// const getDropzoneProps = useMemo( -// () => -// ({ -// onClick = () => {}, -// onDrop = () => {}, -// onDragOver = () => {}, -// onDragLeave = () => {}, -// onKeyDown = () => {}, -// onFocus = () => {}, -// onBlur = () => {}, -// onDragEnter = () => {}, -// refKey = rootRef, -// ...rest -// } = {}) => ({ -// onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), -// onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), -// onDragEnter: composeDragHandler( -// composeEventHandlers(onDragEnter, onDragEnterCb), -// ), -// onDragOver: composeDragHandler( -// composeEventHandlers(onDragOver, onDragOverCb), -// ), -// onDragLeave: composeDragHandler( -// composeEventHandlers(onDragLeave, onDragLeaveCb), -// ), -// onKeyDown: composeKeyboardHandler( -// composeEventHandlers(onKeyDown, onKeyDownCb), // Done -// ), -// onFocus: composeKeyboardHandler( -// composeEventHandlers(onFocus, onFocusCb), // Done -// ), -// onBlur: composeKeyboardHandler( -// composeEventHandlers(onBlur, onBlurCb), // Done -// ), -// [refKey]: rootRef, -// ...rest, -// }), -// [ -// rootRef, -// onKeyDownCb, -// onFocusCb, -// onBlurCb, -// onClickCb, -// onDragEnterCb, -// onDragOverCb, -// onDragLeaveCb, -// onDropCb, -// noKeyboard, -// noDrag, -// disabled, -// ], -// ); -// // ================================== - -// // ============== INPUT ============== -// const getInputProps = useMemo( -// () => -// ({ -// refKey = 'ref', -// onChange = () => {}, -// onClick = () => {}, -// ...rest -// } = {}) => { -// const inputProps = { -// accept, -// multiple, -// type: 'file', -// style: { display: 'none' }, -// onChange: composeHandler(composeEventHandlers(onChange, onDropCb)), -// onClick: composeHandler( -// composeEventHandlers(onClick, onInputElementClick), -// ), -// autoComplete: 'off', -// tabIndex: -1, -// [refKey]: inputRef, -// }; - -// return { -// ...inputProps, -// ...rest, -// }; -// }, -// [inputRef, accept, onDropCb, disabled], -// ); -// // =================================== - -// return ( -// <> -// -// {renderChildren()} -// -// ); -// }; - -// const CSVReader = useMemo(() => CSVReaderComponent, []) as any; - -// return CSVReader; -// } - -// export function useCSVReader() { -// const CSVReader = useCSVReaderComponent(); - -// return { -// CSVReader, -// }; -// } - -// const initialState = { -// displayProgressBar: 'none', -// progressBarPercentage: 0, - -// isDragActive: false, -// isFileDialogActive: false, -// isFocused: false, - -// draggedFiles: [], -// acceptedFiles: [], -// acceptedFile: null, - -// // isDragAccept: false, -// // isDragReject: false, -// // fileRejections: [], -// }; - -// function reducer(state: any, action: any) { -// switch (action.type) { -// case 'openDialog': -// return { -// ...state, -// isFileDialogActive: true, -// }; -// case 'closeDialog': -// return { -// ...state, -// isFileDialogActive: false, -// }; -// case 'setFiles': -// return { -// ...state, -// acceptedFiles: action.acceptedFiles, -// fileRejections: action.fileRejections, -// }; -// case 'setFile': -// return { -// ...state, -// acceptedFile: action.acceptedFile, -// }; -// case 'setDisplayProgressBar': -// return { -// ...state, -// displayProgressBar: action.displayProgressBar, -// }; -// case 'setProgressBarPercentage': -// return { -// ...state, -// progressBarPercentage: action.progressBarPercentage, -// }; -// case 'setDraggedFiles': -// /* eslint no-case-declarations: 0 */ -// const { isDragActive, draggedFiles } = action; -// return { -// ...state, -// draggedFiles, -// isDragActive, -// }; -// case 'focus': -// return { -// ...state, -// isFocused: true, -// }; -// case 'blur': -// return { -// ...state, -// isFocused: false, -// }; -// // case 'reset': -// // return { -// // ...initialState -// // } -// default: -// return state; -// } -// } - - -// 999999999999999999999999999999999999 diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 9b02e8e..9c88875 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -4,7 +4,6 @@ import React, { useCallback, useMemo, useEffect, - useState, ReactNode, useRef, } from 'react'; @@ -31,11 +30,9 @@ export interface Props { children: (fn: any) => void | ReactNode; accept?: string; config?: CustomConfig; - minSize?: number; maxSize?: number; maxFiles?: number; - disabled?: boolean; noClick?: boolean; noDrag?: boolean; @@ -43,7 +40,6 @@ export interface Props { noKeyboard?: boolean; multiple?: boolean; preventDropOnDocument?: boolean; - onUploadAccepted?: (data: ParseResult, file?: File, event?: Event) => void; onDropAccepted?: ( data: ParseResult, @@ -57,73 +53,38 @@ export interface Props { onDragLeave?: (event?: DragEvent) => void; } -export interface Api { - accept?: string; - setAccept?: () => void; - config?: CustomConfig; - setConfig?: () => void; - disabled?: boolean; - setDisabled?: () => void; - minSize?: number; - setMinSize?: () => void; - maxSize?: number; - setMaxSize?: () => void; - maxFiles?: number; - setMaxFiles?: () => void; - noClick?: boolean; - setNoClick?: () => void; - noDrag?: boolean; - setNoDrag?: () => void; - multiple?: boolean; - setMultiple?: () => void; -} - export interface ProgressBarComponentProp { style?: any; className?: string; } -function useCSVReaderComponent(api: Api) { - const CSVReaderComponent = (props: Props) => { +function useCSVReaderComponent() { + const CSVReaderComponent = ({ + children, + accept = DEFAULT_ACCEPT, + config = {}, + minSize = 0, + maxSize = Infinity, + maxFiles = 1, + disabled = false, + noClick = false, + noDrag = false, + noDragEventsBubbling = false, + noKeyboard = false, + multiple = false, + preventDropOnDocument = true, + onUploadAccepted, + validator, + onDropRejected, + onDropAccepted, + onDragEnter, + onDragOver, + onDragLeave, + }: Props) => { const inputRef: any = useRef(null); const rootRef: any = useRef(null); const dragTargetsRef = useRef([]); - const { - accept, - setAccept, - config, - setConfig, - disabled, - setDisabled, - minSize, - setMinSize, - maxSize, - setMaxSize, - maxFiles, - setMaxFiles, - noClick, - setNoClick, - noDrag, - setNoDrag, - multiple, - setMultiple, - } = CSVReader.api; - - const { - children, - onDropAccepted, - onUploadAccepted, - onDropRejected, - noDragEventsBubbling, - validator, - onDragEnter, - onDragOver, - onDragLeave, - noKeyboard = false, - preventDropOnDocument = true, - } = props; - const [state, dispatch] = useReducer(reducer, initialState); const { acceptedFile, @@ -132,30 +93,6 @@ function useCSVReaderComponent(api: Api) { draggedFiles, } = state; - useEffect(() => { - const { - accept, - config, - disabled, - minSize, - maxSize, - maxFiles, - noClick, - noDrag, - multiple, - } = props; - - accept && setAccept(accept); - config && setConfig(config); - disabled && setDisabled(disabled); - minSize && setMinSize(minSize); - maxSize && setMaxSize(maxSize); - maxFiles && setMaxFiles(maxFiles); - noClick && setNoClick(noClick); - noDrag && setNoDrag(noDrag); - multiple && setMultiple(multiple); - }, []); - const onDocumentDrop = (event: DragEvent) => { 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 @@ -269,7 +206,7 @@ function useCSVReaderComponent(api: Api) { }, [inputRef, noClick]); const onDropCb = useCallback( - (event) => { + (event: any) => { allowDrop(event); setProgressBarPercentage(0); @@ -427,7 +364,7 @@ function useCSVReaderComponent(api: Api) { ); const onInputElementClick = useCallback((event) => { - event.stopPropagation(); + stopPropagation(event); }, []); // ==================================== @@ -535,7 +472,7 @@ function useCSVReaderComponent(api: Api) { return; } - if (event.keyCode === 32 || event.keyCode === 13) { + if (event.key === 'Space' || event.key === 'Enter') { event.preventDefault(); openFileDialog(); } @@ -648,47 +585,13 @@ function useCSVReaderComponent(api: Api) { const CSVReader = useMemo(() => CSVReaderComponent, []) as any; - CSVReader.api = api; - return CSVReader; } -export function useCSVReader() { - const [accept, setAccept] = useState(DEFAULT_ACCEPT); - const [config, setConfig] = useState({}); - const [disabled, setDisabled] = useState(false); - const [minSize, setMinSize] = useState(0); - const [maxSize, setMaxSize] = useState(Infinity); - const [maxFiles, setMaxFiles] = useState(1); - const [noClick, setNoClick] = useState(false); - const [noDrag, setNoDrag] = useState(false); - const [multiple, setMultiple] = useState(false); - - const api = { - accept, - setAccept, - config, - setConfig, - disabled, - setDisabled, - minSize, - setMinSize, - maxSize, - setMaxSize, - multiple, - setMultiple, - maxFiles, - setMaxFiles, - noClick, - setNoClick, - noDrag, - setNoDrag, - } as Api; - - const CSVReader = useCSVReaderComponent(api); +export function useCSVReader() { + const CSVReader = useCSVReaderComponent(); return { - ...api, CSVReader, }; } @@ -696,11 +599,9 @@ export function useCSVReader() { const initialState = { displayProgressBar: 'none', progressBarPercentage: 0, - isDragActive: false, isFileDialogActive: false, isFocused: false, - draggedFiles: [], acceptedFiles: [], acceptedFile: null, From 11dffaa3dc264576774ba3afa08e3afa0c76aaea Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 3 Jan 2022 00:22:03 +0700 Subject: [PATCH 093/140] Add remove file programmatically --- src/CSVReader.tsx | 4 +- src/react-papaparse.ts | 1 + src/useCSVReader.tsx | 84 +++++------ src/utils.ts | 2 +- supports/create-next-app/pages/index.tsx | 171 +++++++++++++++++++---- 5 files changed, 178 insertions(+), 84 deletions(-) diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx index 45b9c1a..24e2a2d 100644 --- a/src/CSVReader.tsx +++ b/src/CSVReader.tsx @@ -1,7 +1,7 @@ import React, { CSSProperties } from 'react'; import PapaParse, { ParseResult } from 'papaparse'; import { CustomConfig } from './model'; -import getSize, { lightenDarkenColor } from './utils'; +import { formatFileSize, lightenDarkenColor } from './utils'; import RemoveIcon from './RemoveIcon'; import ProgressBar from './ProgressBar'; @@ -368,7 +368,7 @@ export default class CSVReader extends React.Component< displayFileInfo = (file: any) => { if (!this.childrenIsFunction()) { - this.fileSizeInfoRef.current.innerHTML = getSize(file.size); + this.fileSizeInfoRef.current.innerHTML = formatFileSize(file.size); this.fileNameInfoRef.current.innerHTML = file.name; } }; diff --git a/src/react-papaparse.ts b/src/react-papaparse.ts index 590e6ff..acd9ca9 100644 --- a/src/react-papaparse.ts +++ b/src/react-papaparse.ts @@ -21,3 +21,4 @@ export { jsonToCSV } from './jsonToCSV'; export { usePapaParse } from './usePapaParse'; export { useCSVDownloader } from './useCSVDownloader'; export { useCSVReader } from './useCSVReader'; +export { formatFileSize } from './utils'; diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 9c88875..7ed3a50 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -40,13 +40,12 @@ export interface Props { noKeyboard?: boolean; multiple?: boolean; preventDropOnDocument?: boolean; - onUploadAccepted?: (data: ParseResult, file?: File, event?: Event) => void; - onDropAccepted?: ( + onUploadAccepted?: ( data: ParseResult, file?: File, event?: DragEvent | Event, ) => void; - onDropRejected?: (file?: File, event?: DragEvent | Event) => void; + onUploadRejected?: (file?: File, event?: DragEvent | Event) => void; validator?: (file: File) => void; onDragEnter?: (event?: DragEvent) => void; onDragOver?: (event?: DragEvent) => void; @@ -75,8 +74,7 @@ function useCSVReaderComponent() { preventDropOnDocument = true, onUploadAccepted, validator, - onDropRejected, - onDropAccepted, + onUploadRejected, onDragEnter, onDragOver, onDragLeave, @@ -167,17 +165,12 @@ function useCSVReaderComponent() { }; const renderChildren = () => { - return onUploadAccepted - ? children({ - getButtonProps, - acceptedFile, - ProgressBar: ProgressBarComponent, - }) - : children({ - getDropzoneProps, - acceptedFile, - ProgressBar: ProgressBarComponent, - }); + return children({ + getRootProps, + acceptedFile, + ProgressBar: ProgressBarComponent, + getRemoveFileProps, + }); }; // Fn for opening the file dialog programmatically @@ -268,14 +261,11 @@ function useCSVReaderComponent() { // onDrop(acceptedFiles, fileRejections, event) // } - if (fileRejections.length > 0 && onDropRejected) { - onDropRejected(fileRejections, event); + if (fileRejections.length > 0 && onUploadRejected) { + onUploadRejected(fileRejections, event); } - if ( - acceptedFiles.length > 0 && - (onUploadAccepted || onDropAccepted) - ) { + if (acceptedFiles.length > 0 && onUploadAccepted) { let configs = {} as any; const data: any = []; const errors: any = []; @@ -296,11 +286,7 @@ function useCSVReaderComponent() { ? config.complete : () => { const obj = { data, errors, meta }; - if (!onDropAccepted && onUploadAccepted) { - onUploadAccepted(obj, file); - } else if (onDropAccepted && !onUploadAccepted) { - onDropAccepted(obj, file); - } + onUploadAccepted(obj, file); }, step: config?.step ? config.step @@ -318,11 +304,7 @@ function useCSVReaderComponent() { ); // setProgressBarPercentage(percentage); if (data.length === config.preview) { - if (!onDropAccepted && onUploadAccepted) { - onUploadAccepted(data, file); - } else if (onDropAccepted && !onUploadAccepted) { - onDropAccepted(data, file); - } + onUploadAccepted(data, file); } } else { const cursor = row.meta.cursor; @@ -359,7 +341,6 @@ function useCSVReaderComponent() { maxFiles, validator, onUploadAccepted, - onDropAccepted, ], ); @@ -368,18 +349,7 @@ function useCSVReaderComponent() { }, []); // ==================================== - // ============== BUTTON ============== - const getButtonProps = useMemo( - () => - ({ onClick = () => {}, ...rest } = {}) => ({ - onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), - ...rest, - }), - [onClickCb], - ); - // ==================================== - - // ============== DROP ============== + // ============== BUTTON | DROP ============== const composeKeyboardHandler = (fn: any) => { return noKeyboard ? null : composeHandler(fn); }; @@ -489,7 +459,7 @@ function useCSVReaderComponent() { dispatch({ type: 'blur' }); }, []); - const getDropzoneProps = useMemo( + const getRootProps = useMemo( () => ({ onClick = () => {}, @@ -575,6 +545,20 @@ function useCSVReaderComponent() { ); // =================================== + const removeFileProgrammaticallyCb = useCallback(() => { + inputRef.current.value = ''; + dispatch({ type: 'reset' }); + }, []); + + const getRemoveFileProps = useMemo( + () => + ({ onClick = () => {}, ...rest } = {}) => ({ + onClick: composeHandler(composeEventHandlers(onClick, removeFileProgrammaticallyCb)), + ...rest, + }), + [removeFileProgrammaticallyCb], + ); + return ( <> @@ -662,10 +646,10 @@ function reducer(state: any, action: any) { ...state, isFocused: false, }; - // case 'reset': - // return { - // ...initialState - // } + case 'reset': + return { + ...initialState, + }; default: return state; } diff --git a/src/utils.ts b/src/utils.ts index 256b02f..086c6ab 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -4,7 +4,7 @@ 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 default function getSize(size: number) { +export function formatFileSize(size: number) { const sizeKb = 1024; const sizeMb = sizeKb * sizeKb; const sizeGb = sizeMb * sizeKb; diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index a8a9750..a1a7480 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,24 +1,28 @@ -import React, { useState } from 'react' +import React, { CSSProperties, useState, useRef, ReactNode } from 'react'; import { usePapaParse, useCSVDownloader, useCSVReader, -} from 'react-papaparse' + formatFileSize, +} from 'react-papaparse'; export default function Home() { + const inputRef = useRef(null); const { CSVDownloader, Type } = useCSVDownloader(); const { CSVReader } = useCSVReader(); const { readString } = usePapaParse(); + const [hovered, setHovered] = useState(false); + const handleOnError = ( - err: any, + err: any // file, // inputElem, // reason ) => { - console.log(err) - } + console.log(err); + }; const handleReadString = () => { const csvString = `Column 1,Column 2,Column 3,Column 4 @@ -34,12 +38,84 @@ export default function Home() { console.log(results); console.log('---------------------------'); }, - }) + }); + }; + + const styles = { + zone: { + alignItems: 'center', + borderStyle: 'dashed', + borderWidth: 2, + borderRadius: 20, + borderColor: '#CCC', + display: 'flex', + flexDirection: 'column', + height: '100%', + justifyContent: 'center', + padding: 20, + } as CSSProperties, + file: { + background: 'linear-gradient(to bottom, #EEE, #DDD)', + borderRadius: 20, + display: 'flex', + height: 120, + width: 120, + position: 'relative', + zIndex: 10, + flexDirection: 'column', + justifyContent: 'center', + } as CSSProperties, + info: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + size: { + backgroundColor: 'rgba(255, 255, 255, 0.4)', + borderRadius: 3, + marginBottom: '0.5em', + justifyContent: 'center', + display: 'flex', + } as CSSProperties, + name: { + backgroundColor: 'rgba(255, 255, 255, 0.4)', + borderRadius: 3, + fontSize: 12, + marginBottom: '0.5em', + } as CSSProperties, + progressBar: { + bottom: 14, + position: 'absolute', + width: '100%', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + hover: { + borderColor: '#686868', + } as CSSProperties, + default: { + borderColor: '#CCC', + } as CSSProperties, + // defaultCursor: { + // cursor: 'default', + // } as CSSProperties, + // pointerCursor: { + // cursor: 'pointer', + // } as CSSProperties, + // removeButton: { + // height: 23, + // position: 'absolute', + // right: 6, + // top: 6, + // width: 23, + // } as CSSProperties, }; return ( <> - {/*
+
- {({ getButtonProps, acceptedFile, ProgressBar }: any) => ( + {({ getRootProps, acceptedFile, ProgressBar }: any) => ( <>
*/} - {/*
*/} +
+
{ + onUploadAccepted={(results: any) => { console.log('---------------------------'); console.log(results); console.log('---------------------------'); + setHovered(false); }} - noDrag + onDragOver={(event: DragEvent) => { + event.preventDefault(); + setHovered(true); + }} + onDragLeave={(event: DragEvent) => { + event.preventDefault(); + setHovered(false); + }} + // noDrag > - {({ getDropzoneProps, acceptedFile, ProgressBar }: any) => ( + {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps }: any) => ( <>
- - {acceptedFile ? acceptedFile.name : 'Drop CSV file here or click to upload.'} - - + {acceptedFile ? ( + <> +
+
+ + {formatFileSize(acceptedFile.size)} + + {acceptedFile.name} +
+
+ +
+
+ + ) : ( + 'Drop CSV file here or click to upload.' + //
+ //
+ // + // 1 KB + // + // + // normal.csv + // + //
+ //
+ // + //
+ //
+ )}
+ )}
- {/*
*/} +
- ) + ); } From 6a505a42e089190321ffee288accb7d369b0f160 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 3 Jan 2022 23:07:56 +0700 Subject: [PATCH 094/140] Fix breaking change in webpack 5 --- package.json | 1 - rollup.config.js | 13 +- supports/create-react-app/README.md | 24 ++-- supports/create-react-app/package.json | 16 +-- supports/create-react-app/src/App.js | 192 +++++++++++++------------ 5 files changed, 125 insertions(+), 121 deletions(-) diff --git a/package.json b/package.json index 1820be0..85990b5 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,6 @@ "@babel/preset-env": "^7.15.0", "@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-commonjs": "^20.0.0", - "@rollup/plugin-node-resolve": "^13.0.4", "@types/jest": "^27.0.2", "@types/react": "^17.0.19", "@types/react-dom": "^17.0.9", diff --git a/rollup.config.js b/rollup.config.js index 1422490..4e23d92 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,9 +1,11 @@ import typescript from 'rollup-plugin-typescript2'; import babel from '@rollup/plugin-babel'; import commonjs from '@rollup/plugin-commonjs'; -import resolve from '@rollup/plugin-node-resolve'; -import builtins from 'builtin-modules' import { terser } from "rollup-plugin-terser"; + +// import builtins from 'builtin-modules' +// import { nodeResolve } from '@rollup/plugin-node-resolve'; + import pkg from './package.json'; export default { @@ -35,7 +37,8 @@ export default { // }, // }, ], - external: builtins, + // external: builtins, + external: ['react', 'papaparse'], plugins: [ typescript({ tsconfig: './tsconfig.json', @@ -44,9 +47,7 @@ export default { babel({ exclude: 'node_modules/**' }), - resolve({ - preferBuiltins: true - }), + // nodeResolve(), commonjs({ extensions: ['.js', '.ts', '.tsx'] }), diff --git a/supports/create-react-app/README.md b/supports/create-react-app/README.md index 02aac3f..58beeac 100644 --- a/supports/create-react-app/README.md +++ b/supports/create-react-app/README.md @@ -6,20 +6,20 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo In the project directory, you can run: -### `yarn start` +### `npm start` Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in the browser. +Open [http://localhost:3000](http://localhost:3000) to view it in your browser. -The page will reload if you make edits.\ -You will also see any lint errors in the console. +The page will reload when you make changes.\ +You may also see any lint errors in the console. -### `yarn test` +### `npm test` Launches the test runner in the interactive watch mode.\ See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. -### `yarn build` +### `npm run build` Builds the app for production to the `build` folder.\ It correctly bundles React in production mode and optimizes the build for the best performance. @@ -29,15 +29,15 @@ Your app is ready to be deployed! See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. -### `yarn eject` +### `npm run eject` -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** +**Note: this is a one-way operation. Once you `eject`, you can't go back!** -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. +If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. +Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. +You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. ## Learn More @@ -65,6 +65,6 @@ This section has moved here: [https://facebook.github.io/create-react-app/docs/a This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) -### `yarn build` fails to minify +### `npm run build` fails to minify This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) diff --git a/supports/create-react-app/package.json b/supports/create-react-app/package.json index 81bcc76..e075974 100644 --- a/supports/create-react-app/package.json +++ b/supports/create-react-app/package.json @@ -1,19 +1,19 @@ { - "name": "react-papaparse", + "name": "create-react-app", "version": "0.1.0", "private": true, "dependencies": { - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^11.1.0", - "@testing-library/user-event": "^12.1.10", + "@testing-library/jest-dom": "^5.16.1", + "@testing-library/react": "^12.1.2", + "@testing-library/user-event": "^13.5.0", "react": "^17.0.2", "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "web-vitals": "^1.0.1", - "react-papaparse": "file:../.." + "react-papaparse": "file:../..", + "react-scripts": "5.0.0", + "web-vitals": "^2.1.2" }, "scripts": { - "start": "react-scripts start", + "dev": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" diff --git a/supports/create-react-app/src/App.js b/supports/create-react-app/src/App.js index 3db223c..3b8068d 100644 --- a/supports/create-react-app/src/App.js +++ b/supports/create-react-app/src/App.js @@ -1,26 +1,11 @@ -import React, { useState } from 'react'; -import { CSVReader, CSVDownloader } from 'react-papaparse' +import React, { useState } from 'react' +import { CSVReader, CSVDownloader, readString } from 'react-papaparse' -const buttonRef= React.createRef() - -function App() { - const [isReset, setIsReset] = useState(true); +export default function App() { + const [isReset, setIsReset] = useState(false); const handleReset = () => { - setIsReset(!isReset); - } - - const handleOpenDialog = (e) => { - // Note that the ref is set async, so it might be null at some point - if (buttonRef.current) { - buttonRef.current.open(e) - } - } - - const handleOnFileLoad = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') + setIsReset(!isReset) } const handleOnDrop = (data) => { @@ -29,7 +14,12 @@ function App() { console.log('---------------------------') } - const handleOnError = (err, file, inputElem, reason) => { + const handleOnError = ( + err, + // file, + // inputElem, + // reason + ) => { console.log(err) } @@ -39,83 +29,73 @@ function App() { console.log('---------------------------') } - const handleRemoveFile = (e) => { - // Note that the ref is set async, so it might be null at some point - if (buttonRef.current) { - buttonRef.current.removeFile(e) - } - } + const handleClick = () => { + const csvString = `Column 1,Column 2,Column 3,Column 4 +1-1,1-2,1-3,1-4 +2-1,2-2,2-3,2-4 +3-1,3-2,3-3,3-4 +4,5,6,7`; + + readString(csvString, { + worker: true, + complete: (results) => { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }, + }) + }; return ( <> - {({ file }) => ( - - )} + Click to upload. + Download - + + Download + + { + return [ + { + "Column 1": "1-1", + "Column 2": "1-2", + "Column 3": "1-3", + "Column 4": "1-4", + } + ]} + } + > + Download + + - ); + ) } - -export default App; From 8b9bb3f844976370de9ccc074552afd6eae2127b Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 3 Jan 2022 23:26:59 +0700 Subject: [PATCH 095/140] Update v3.18.2 --- CHANGELOG.md | 10 ++++++++++ README.md | 4 ++-- package.json | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a320d55..8aee13b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 3.18.2 (2022-01-04) + +### ✨ Bugs + + * Fix breaking change with webpack 5 + +Credits + +* ax0n-pr1me [@ax0n-pr1me](https://github.com/ax0n-pr1me) + ## 3.18.1 (2021-11-07) ### ✨ Bugs diff --git a/README.md b/README.md index ec4612c..95aef9b 100644 --- a/README.md +++ b/README.md @@ -499,9 +499,9 @@ readRemoteFile('http://example.com/big.csv', { ## 📜 Changelog -Latest version 3.18.1 (2021-11-07): +Latest version 3.18.2 (2022-01-04): - * Fix a bug when component is unmounted immediately after file load in CSVReader + * Fix breaking change with webpack 5 Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/react-papaparse/blob/master/CHANGELOG.md). diff --git a/package.json b/package.json index 85990b5..4d95d82 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-papaparse", - "version": "3.18.1", + "version": "3.18.2", "description": "The fastest in-browser CSV (or delimited text) parser for React. It is full of useful features such as CSVReader, CSVDownloader, readString, jsonToCSV, readRemoteFile, ... etc.", "author": "Bunlong ", "license": "MIT", From 3c674520a94d5364aa3cd60bcc4bec70b16d271c Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 00:31:20 +0700 Subject: [PATCH 096/140] Add Remove button --- src/Remove.tsx | 27 +++++ src/react-papaparse.ts | 2 +- src/useCSVReader.tsx | 22 ++-- supports/create-next-app/pages/index.tsx | 122 +++++++++++++---------- 4 files changed, 112 insertions(+), 61 deletions(-) create mode 100644 src/Remove.tsx diff --git a/src/Remove.tsx b/src/Remove.tsx new file mode 100644 index 0000000..93e369e --- /dev/null +++ b/src/Remove.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +export interface Props { + color?: string; + width?: number; + height?: number; +} + +export default function Remove({ color, width = 23, height = 23 }: Props) { + return ( + + + + + ); +} diff --git a/src/react-papaparse.ts b/src/react-papaparse.ts index acd9ca9..03efea4 100644 --- a/src/react-papaparse.ts +++ b/src/react-papaparse.ts @@ -21,4 +21,4 @@ export { jsonToCSV } from './jsonToCSV'; export { usePapaParse } from './usePapaParse'; export { useCSVDownloader } from './useCSVDownloader'; export { useCSVReader } from './useCSVReader'; -export { formatFileSize } from './utils'; +export { formatFileSize, lightenDarkenColor } from './utils'; diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 7ed3a50..f8ec711 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -20,6 +20,7 @@ import { onDocumentDragOver, } from './utils'; import ProgressBar from './ProgressBar'; +import Remove, { Props as RemoveComponentProps } from './Remove'; // 'text/csv' for MacOS // '.csv' for Linux @@ -150,26 +151,27 @@ function useCSVReaderComponent() { }); }; - const ProgressBarComponent = ({ - style, - className, - }: ProgressBarComponentProp) => { + const ProgressBarComponent = (props: ProgressBarComponentProp) => { return ( ); }; + const RemoveComponent = (props: RemoveComponentProps) => { + return ; + }; + const renderChildren = () => { return children({ getRootProps, acceptedFile, ProgressBar: ProgressBarComponent, getRemoveFileProps, + Remove: RemoveComponent, }); }; @@ -545,15 +547,19 @@ function useCSVReaderComponent() { ); // =================================== - const removeFileProgrammaticallyCb = useCallback(() => { + const removeFileProgrammaticallyCb = useCallback((event: Event) => { inputRef.current.value = ''; dispatch({ type: 'reset' }); + // To prevent a parents onclick event from firing when a child is clicked + event.stopPropagation(); }, []); const getRemoveFileProps = useMemo( () => ({ onClick = () => {}, ...rest } = {}) => ({ - onClick: composeHandler(composeEventHandlers(onClick, removeFileProgrammaticallyCb)), + onClick: composeHandler( + composeEventHandlers(onClick, removeFileProgrammaticallyCb), + ), ...rest, }), [removeFileProgrammaticallyCb], diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index a1a7480..bbf6573 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,29 +1,22 @@ -import React, { CSSProperties, useState, useRef, ReactNode } from 'react'; +import React, { useState, CSSProperties } from 'react'; import { usePapaParse, useCSVDownloader, useCSVReader, formatFileSize, + lightenDarkenColor, } from 'react-papaparse'; +const GREY = '#CCC'; +const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; +const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; +const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(DEFAULT_REMOVE_HOVER_COLOR, 40) +const GREY_DIM = '#686868'; + export default function Home() { - const inputRef = useRef(null); const { CSVDownloader, Type } = useCSVDownloader(); - const { CSVReader } = useCSVReader(); const { readString } = usePapaParse(); - - const [hovered, setHovered] = useState(false); - - const handleOnError = ( - err: any - // file, - // inputElem, - // reason - ) => { - console.log(err); - }; - const handleReadString = () => { const csvString = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 @@ -41,13 +34,14 @@ export default function Home() { }); }; + const { CSVReader } = useCSVReader(); + const [zoneHover, setZoneHover] = useState(false); + const [removeHoverColor, setRemoveHoverColor] = useState(DEFAULT_REMOVE_HOVER_COLOR); const styles = { zone: { alignItems: 'center', - borderStyle: 'dashed', - borderWidth: 2, + border: `2px dashed ${GREY}`, borderRadius: 20, - borderColor: '#CCC', display: 'flex', flexDirection: 'column', height: '100%', @@ -73,14 +67,14 @@ export default function Home() { paddingRight: 10, } as CSSProperties, size: { - backgroundColor: 'rgba(255, 255, 255, 0.4)', + backgroundColor: GREY_LIGHT, borderRadius: 3, marginBottom: '0.5em', justifyContent: 'center', display: 'flex', } as CSSProperties, name: { - backgroundColor: 'rgba(255, 255, 255, 0.4)', + backgroundColor: GREY_LIGHT, borderRadius: 3, fontSize: 12, marginBottom: '0.5em', @@ -92,11 +86,18 @@ export default function Home() { paddingLeft: 10, paddingRight: 10, } as CSSProperties, - hover: { - borderColor: '#686868', + zoneHover: { + borderColor: GREY_DIM, } as CSSProperties, default: { - borderColor: '#CCC', + borderColor: GREY, + } as CSSProperties, + remove: { + height: 23, + position: 'absolute', + right: 6, + top: 6, + width: 23, } as CSSProperties, // defaultCursor: { // cursor: 'default', @@ -104,19 +105,12 @@ export default function Home() { // pointerCursor: { // cursor: 'pointer', // } as CSSProperties, - // removeButton: { - // height: 23, - // position: 'absolute', - // right: 6, - // top: 6, - // width: 23, - // } as CSSProperties, }; return ( <>
- Download ( Button ) - - */} + {/* Download ( Link ) - - */} + {/* { return [ @@ -179,12 +173,12 @@ export default function Home() { type={Type.Button} > Download ( Data as a Function/Callback ) - + */}
-
+ {/*
-
-
+
*/} + {/*
{ console.log('---------------------------'); @@ -226,33 +220,33 @@ export default function Home() { )} -
+
*/}
{ console.log('---------------------------'); console.log(results); console.log('---------------------------'); - setHovered(false); + setZoneHover(false); }} onDragOver={(event: DragEvent) => { event.preventDefault(); - setHovered(true); + setZoneHover(true); }} onDragLeave={(event: DragEvent) => { event.preventDefault(); - setHovered(false); + setZoneHover(false); }} // noDrag > - {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps }: any) => ( + {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( <>
{acceptedFile ? ( @@ -267,28 +261,52 @@ export default function Home() {
+
{ + event.preventDefault(); + setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); + }} + onMouseOut={(event) => { + event.preventDefault(); + setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); + }} + > + +
) : ( 'Drop CSV file here or click to upload.' + //
//
// // 1 KB // - // - // normal.csv - // + // normal.csv //
//
- // + // + //
+ //
{ + // event.preventDefault(); + // setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); + // }} + // onMouseOut={(event) => { + // event.preventDefault(); + // setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); + // }} + // > + // //
//
)}
- )} From 5f1bb19957f192cdcd7c71b0b8e745383e18d6b0 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 17:56:45 +0700 Subject: [PATCH 097/140] Update CSVDownloader button --- README.md | 87 ++++++++++++------------ supports/create-next-app/pages/index.tsx | 15 ++-- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 95aef9b..663846a 100644 --- a/README.md +++ b/README.md @@ -306,48 +306,51 @@ Just pass in the js object with an optional [configuration](https://react-papapa #### Button ```javascript -import React, { Component } from 'react' - -import { CSVDownloader } from 'react-papaparse' - -export default class CSVDownloader extends Component { - render() { - return ( - - Download - - ) - } +import { useCSVDownloader } from 'react-papaparse'; + +export default function CSVDownloader() { + const { CSVDownloader, Type } = useCSVDownloader(); + + return ( + + Download ( Button ) + + ); } ``` diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index bbf6573..af0874c 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -111,6 +111,14 @@ export default function Home() { <>
{/* Download ( Button ) */} From 1f01a15404e15ede0fd06f84c45a37ae2764c554 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 21:26:26 +0700 Subject: [PATCH 098/140] Update CSVDownloader link --- README.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 663846a..9ce263a 100644 --- a/README.md +++ b/README.md @@ -348,7 +348,7 @@ export default function CSVDownloader() { }, ]} > - Download ( Button ) + Download ); } @@ -357,26 +357,25 @@ export default function CSVDownloader() { #### Link ```javascript -import React, { Component } from 'react' +import { useCSVDownloader } from 'react-papaparse'; -import { CSVDownloader } from 'react-papaparse' +export default function CSVDownloader() { + const { CSVDownloader, Type } = useCSVDownloader(); -export default class CSVDownloader extends Component { - render() { - return ( - - Download - - ) - } + > + Download + + ); } ``` From df7a629feeb6abac401c765ac5550d9957e02bdf Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 21:42:39 +0700 Subject: [PATCH 099/140] Update Data as a Function/Callback --- README.md | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 9ce263a..249e32f 100644 --- a/README.md +++ b/README.md @@ -384,21 +384,29 @@ export default function CSVDownloader() { `data={}` can be a function that returns a data object. ```javascript - { - return [ - { - "Column 1": "1-1", - "Column 2": "1-2", - "Column 3": "1-3", - "Column 4": "1-4", +import { useCSVDownloader } from 'react-papaparse'; + +export default function CSVDownloader() { + const { CSVDownloader } = useCSVDownloader(); + + return ( + { + return [ + { + "Column 1": "1-1", + "Column 2": "1-2", + "Column 3": "1-3", + "Column 4": "1-4", + } + ]} } - ]} - } -> - Download - + > + Download + + ); +} ``` ### 🎀 readString From 3fa74ea236fd0d185aeef0eaef1c127732755135 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 21:59:56 +0700 Subject: [PATCH 100/140] Update readString --- README.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 249e32f..cce72d3 100644 --- a/README.md +++ b/README.md @@ -412,20 +412,30 @@ export default function CSVDownloader() { ### 🎀 readString ```javascript -import { readString } from 'react-papaparse' +import { usePapaParse } from 'react-papaparse'; -const csvString = `Column 1,Column 2,Column 3,Column 4 +export default function ReadString() { + const { readString } = usePapaParse(); + + const handleReadString = () => { + const csvString = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 2-1,2-2,2-3,2-4 3-1,3-2,3-3,3-4 -4,5,6,7` - -readString(csvString, { - worker: true, - complete: (results) => { - console.log(results) - } -}) +4,5,6,7`; + + readString(csvString, { + worker: true, + complete: (results) => { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }, + }); + }; + + return (); +} ``` ### 🎀 readRemoteFile From c04d3facfea3ebc30cf4a56a0b6056fa17d055ad Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 22:34:44 +0700 Subject: [PATCH 101/140] Update readRemoteFile --- README.md | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index cce72d3..707f421 100644 --- a/README.md +++ b/README.md @@ -441,16 +441,26 @@ export default function ReadString() { ### 🎀 readRemoteFile ```javascript -import { readRemoteFile } from 'react-papaparse' +import { usePapaParse } from 'react-papaparse'; -readRemoteFile( - url, - { - complete: (results) => { - console.log('Results:', results) - } - } -) +export default function ReadRemoteFile() { + const { readRemoteFile } = usePapaParse(); + + const handleReadRemoteFile = () => { + readRemoteFile( + url, + { + complete: (results) => { + console.log('---------------------------'); + console.log('Results:', results) + console.log('---------------------------'); + } + } + ); + }; + + return (); +} ``` ### 🎀 jsonToCSV From ab5c1c634fa428f96a9a59b930240854ea3721cc Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 22:53:16 +0700 Subject: [PATCH 102/140] Update jsonToCSV --- README.md | 68 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 707f421..b97694f 100644 --- a/README.md +++ b/README.md @@ -466,36 +466,46 @@ export default function ReadRemoteFile() { ### 🎀 jsonToCSV ```javascript -import { jsonToCSV } from 'react-papaparse' - -const jsonData = `[ - { - "Column 1": "1-1", - "Column 2": "1-2", - "Column 3": "1-3", - "Column 4": "1-4" - }, - { - "Column 1": "2-1", - "Column 2": "2-2", - "Column 3": "2-3", - "Column 4": "2-4" - }, - { - "Column 1": "3-1", - "Column 2": "3-2", - "Column 3": "3-3", - "Column 4": "3-4" - }, - { - "Column 1": 4, - "Column 2": 5, - "Column 3": 6, - "Column 4": 7 - } -]` +import { usePapaParse } from 'react-papaparse'; + +export default function JsonToCSV() { + const { jsonToCSV } = usePapaParse(); -const results = jsonToCSV(jsonData) + const handleJsonToCSV = () => { + const jsonData = `[ + { + "Column 1": "1-1", + "Column 2": "1-2", + "Column 3": "1-3", + "Column 4": "1-4" + }, + { + "Column 1": "2-1", + "Column 2": "2-2", + "Column 3": "2-3", + "Column 4": "2-4" + }, + { + "Column 1": "3-1", + "Column 2": "3-2", + "Column 3": "3-3", + "Column 4": "3-4" + }, + { + "Column 1": 4, + "Column 2": 5, + "Column 3": 6, + "Column 4": 7 + } + ]`; + const results = jsonToCSV(jsonData) + console.log('---------------------------'); + console.log('Results:', results) + console.log('---------------------------'); + }; + + return (); +} ``` #### Header row support From 53e9424ddeb217036851e6e8fdb9879c76f3392f Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 23:07:59 +0700 Subject: [PATCH 103/140] Update Header Row Support --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b97694f..ccf757a 100644 --- a/README.md +++ b/README.md @@ -508,18 +508,24 @@ export default function JsonToCSV() { } ``` -#### Header row support +#### Header Row Support If you tell react-papaparse there is a header row, each row will be organized by field name instead of index. ```javascript +import { usePapaParse } from 'react-papaparse'; + +const { readString } = usePapaParse(); + readString(csvString, { header: true, worker: true, complete: (results) => { - console.log(results) - } -}) + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }, +}); ``` #### Stream From dcc19847ccf080caa03f06e0c499dac35fba88e7 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 23:21:13 +0700 Subject: [PATCH 104/140] Update Stream --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ccf757a..5ca9238 100644 --- a/README.md +++ b/README.md @@ -447,16 +447,13 @@ export default function ReadRemoteFile() { const { readRemoteFile } = usePapaParse(); const handleReadRemoteFile = () => { - readRemoteFile( - url, - { - complete: (results) => { - console.log('---------------------------'); - console.log('Results:', results) - console.log('---------------------------'); - } + readRemoteFile(url, { + complete: (results) => { + console.log('---------------------------'); + console.log('Results:', results) + console.log('---------------------------'); } - ); + }); }; return (); @@ -533,14 +530,18 @@ readString(csvString, { That's what streaming is for. Specify a step callback to receive the results row-by-row. This way, you won't load the whole file into memory and crash the browser. ```javascript -readRemoteFile('http://example.com/big.csv', { +import { usePapaParse } from 'react-papaparse'; + +const { readRemoteFile } = usePapaParse(); + +readRemoteFile(url, { step: (row) => { console.log('Row:', row.data) }, complete: () => { console.log('All done!') } -}) +}); ``` ## 📜 Changelog From b51b1c2b57452f4b8779e6483184b2435290d0a7 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 23:29:31 +0700 Subject: [PATCH 105/140] Update CHANGELOG --- CHANGELOG.md | 11 +++++++++++ README.md | 9 ++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aee13b..879aa92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## 4.0.0 (2022-01-017) + +### ✨ Features + + * Improve code performance + * Rewrite any existing based components to hooks + +Credits + +* [@Bunlong](https://github.com/Bunlong) + ## 3.18.2 (2022-01-04) ### ✨ Bugs diff --git a/README.md b/README.md index 5ca9238..8935a62 100644 --- a/README.md +++ b/README.md @@ -546,18 +546,17 @@ readRemoteFile(url, { ## 📜 Changelog -Latest version 3.18.2 (2022-01-04): +Latest version 4.0.0 (2022-01-17): - * Fix breaking change with webpack 5 + * Improve code performance + * Rewrite any existing based components to hooks Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/react-papaparse/blob/master/CHANGELOG.md). ## 🛣️ Roadmap -### 🆕 v4.0.x +### 🆕 v4.1.x - * Improve code performance - * Rewrite any existing based components to hooks * CSVReader multiple files drag and drop ## ❗ Issues From b4cf2ac166aa041f4ec1d13ae720831866c5c1f5 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 6 Jan 2022 10:45:06 +0700 Subject: [PATCH 106/140] Update Click and Drag Upload --- README.md | 173 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 140 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 8935a62..d0f2df8 100644 --- a/README.md +++ b/README.md @@ -176,40 +176,147 @@ export default class CSVReader extends Component { ![click-and-drag-upload](https://react-papaparse.github.io/static/images/csvreader2.png) ```javascript -import React, { Component } from 'react' - -import { CSVReader } from 'react-papaparse' - -export default class CSVReader extends Component { - handleOnDrop = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - - handleOnError = (err, file, inputElem, reason) => { - console.log(err) - } - - handleOnRemoveFile = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +import React, { useState, CSSProperties } from 'react'; +import { useCSVReader } from 'react-papaparse'; + +const GREY = '#CCC'; +const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; +const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; +const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(DEFAULT_REMOVE_HOVER_COLOR, 40) +const GREY_DIM = '#686868'; + +const styles = { + zone: { + alignItems: 'center', + border: `2px dashed ${GREY}`, + borderRadius: 20, + display: 'flex', + flexDirection: 'column', + height: '100%', + justifyContent: 'center', + padding: 20, + } as CSSProperties, + file: { + background: 'linear-gradient(to bottom, #EEE, #DDD)', + borderRadius: 20, + display: 'flex', + height: 120, + width: 120, + position: 'relative', + zIndex: 10, + flexDirection: 'column', + justifyContent: 'center', + } as CSSProperties, + info: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + size: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + marginBottom: '0.5em', + justifyContent: 'center', + display: 'flex', + } as CSSProperties, + name: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + fontSize: 12, + marginBottom: '0.5em', + } as CSSProperties, + progressBar: { + bottom: 14, + position: 'absolute', + width: '100%', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + zoneHover: { + borderColor: GREY_DIM, + } as CSSProperties, + default: { + borderColor: GREY, + } as CSSProperties, + remove: { + height: 23, + position: 'absolute', + right: 6, + top: 6, + width: 23, + } as CSSProperties, +}; + +export default function CSVReader() { + const { CSVReader } = useCSVReader(); + const [zoneHover, setZoneHover] = useState(false); + const [removeHoverColor, setRemoveHoverColor] = useState(DEFAULT_REMOVE_HOVER_COLOR); - render() { - return ( - - Drop CSV file here or click to upload. - - ) - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + setZoneHover(false); + }} + onDragOver={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(true); + }} + onDragLeave={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(false); + }} + > + {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( + <> +
+ {acceptedFile ? ( + <> +
+
+ + {formatFileSize(acceptedFile.size)} + + {acceptedFile.name} +
+
+ +
+
{ + event.preventDefault(); + setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); + }} + onMouseOut={(event) => { + event.preventDefault(); + setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); + }} + > + +
+
+ + ) : ( + 'Drop CSV file here or click to upload.' + )} +
+ + )} +
+ ); } ``` From 8251638a9eea00ce36c83e24aa8fd8c790c96cb8 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 6 Jan 2022 10:59:59 +0700 Subject: [PATCH 107/140] Update Drag ( No Click ) Upload --- README.md | 169 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 138 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index d0f2df8..801e482 100644 --- a/README.md +++ b/README.md @@ -310,7 +310,7 @@ export default function CSVReader() {
) : ( - 'Drop CSV file here or click to upload.' + 'Drop CSV file here or click to upload' )}
@@ -325,40 +325,147 @@ export default function CSVReader() { ![drag-no-click-upload](https://react-papaparse.github.io/static/images/csvreader3.png) ```javascript -import React, { Component } from 'react' - -import { CSVReader } from 'react-papaparse' +import React, { useState, CSSProperties } from 'react'; +import { useCSVReader } from 'react-papaparse'; -export default class CSVReader extends Component { - handleOnDrop = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +const GREY = '#CCC'; +const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; +const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; +const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(DEFAULT_REMOVE_HOVER_COLOR, 40) +const GREY_DIM = '#686868'; - handleOnError = (err, file, inputElem, reason) => { - console.log(err) - } +const styles = { + zone: { + alignItems: 'center', + border: `2px dashed ${GREY}`, + borderRadius: 20, + display: 'flex', + flexDirection: 'column', + height: '100%', + justifyContent: 'center', + padding: 20, + } as CSSProperties, + file: { + background: 'linear-gradient(to bottom, #EEE, #DDD)', + borderRadius: 20, + display: 'flex', + height: 120, + width: 120, + position: 'relative', + zIndex: 10, + flexDirection: 'column', + justifyContent: 'center', + } as CSSProperties, + info: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + size: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + marginBottom: '0.5em', + justifyContent: 'center', + display: 'flex', + } as CSSProperties, + name: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + fontSize: 12, + marginBottom: '0.5em', + } as CSSProperties, + progressBar: { + bottom: 14, + position: 'absolute', + width: '100%', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + zoneHover: { + borderColor: GREY_DIM, + } as CSSProperties, + default: { + borderColor: GREY, + } as CSSProperties, + remove: { + height: 23, + position: 'absolute', + right: 6, + top: 6, + width: 23, + } as CSSProperties, +}; - handleOnRemoveFile = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +export default function CSVReader() { + const { CSVReader } = useCSVReader(); + const [zoneHover, setZoneHover] = useState(false); + const [removeHoverColor, setRemoveHoverColor] = useState(DEFAULT_REMOVE_HOVER_COLOR); - render() { - return ( - - Drop CSV file here to upload. - - ) - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + setZoneHover(false); + }} + onDragOver={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(true); + }} + onDragLeave={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(false); + }} + > + {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( + <> +
+ {acceptedFile ? ( + <> +
+
+ + {formatFileSize(acceptedFile.size)} + + {acceptedFile.name} +
+
+ +
+
{ + event.preventDefault(); + setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); + }} + onMouseOut={(event) => { + event.preventDefault(); + setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); + }} + > + +
+
+ + ) : ( + 'Drop CSV file here to upload' + )} +
+ + )} +
+ ); } ``` From 756824c944c0d852ec0767e5c29ac874c46eb87f Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 6 Jan 2022 11:06:48 +0700 Subject: [PATCH 108/140] Update Drag ( No Click ) Upload --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 801e482..91e5ca7 100644 --- a/README.md +++ b/README.md @@ -419,6 +419,7 @@ export default function CSVReader() { event.preventDefault(); setZoneHover(false); }} + noClick > {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( <> From 513d3286fdd5d0b39537a05edc116783a34876ad Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 6 Jan 2022 11:08:36 +0700 Subject: [PATCH 109/140] Update Click ( No Drag ) Upload --- README.md | 168 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 138 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 91e5ca7..3dadebf 100644 --- a/README.md +++ b/README.md @@ -475,40 +475,148 @@ export default function CSVReader() { ![click-no-drag-upload](https://react-papaparse.github.io/static/images/csvreader4.png) ```javascript -import React, { Component } from 'react' - -import { CSVReader } from 'react-papaparse' +import React, { useState, CSSProperties } from 'react'; +import { useCSVReader } from 'react-papaparse'; -export default class CSVReader extends Component { - handleOnDrop = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +const GREY = '#CCC'; +const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; +const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; +const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(DEFAULT_REMOVE_HOVER_COLOR, 40) +const GREY_DIM = '#686868'; - handleOnError = (err, file, inputElem, reason) => { - console.log(err) - } +const styles = { + zone: { + alignItems: 'center', + border: `2px dashed ${GREY}`, + borderRadius: 20, + display: 'flex', + flexDirection: 'column', + height: '100%', + justifyContent: 'center', + padding: 20, + } as CSSProperties, + file: { + background: 'linear-gradient(to bottom, #EEE, #DDD)', + borderRadius: 20, + display: 'flex', + height: 120, + width: 120, + position: 'relative', + zIndex: 10, + flexDirection: 'column', + justifyContent: 'center', + } as CSSProperties, + info: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + size: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + marginBottom: '0.5em', + justifyContent: 'center', + display: 'flex', + } as CSSProperties, + name: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + fontSize: 12, + marginBottom: '0.5em', + } as CSSProperties, + progressBar: { + bottom: 14, + position: 'absolute', + width: '100%', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + zoneHover: { + borderColor: GREY_DIM, + } as CSSProperties, + default: { + borderColor: GREY, + } as CSSProperties, + remove: { + height: 23, + position: 'absolute', + right: 6, + top: 6, + width: 23, + } as CSSProperties, +}; - handleOnRemoveFile = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +export default function CSVReader() { + const { CSVReader } = useCSVReader(); + const [zoneHover, setZoneHover] = useState(false); + const [removeHoverColor, setRemoveHoverColor] = useState(DEFAULT_REMOVE_HOVER_COLOR); - render() { - return ( - - Click to upload. - - ) - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + setZoneHover(false); + }} + onDragOver={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(true); + }} + onDragLeave={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(false); + }} + noDrag + > + {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( + <> +
+ {acceptedFile ? ( + <> +
+
+ + {formatFileSize(acceptedFile.size)} + + {acceptedFile.name} +
+
+ +
+
{ + event.preventDefault(); + setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); + }} + onMouseOut={(event) => { + event.preventDefault(); + setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); + }} + > + +
+
+ + ) : ( + 'Click to upload' + )} +
+ + )} +
+ ); } ``` From 4421837249a78e3ccfc5f37d81624239956fe32a Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 6 Jan 2022 20:56:46 +0700 Subject: [PATCH 110/140] Add remove button --- supports/create-next-app/pages/index.tsx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index af0874c..2234013 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -179,7 +179,7 @@ export default function Home() { {/*
*/} - {/*
+
{ console.log('---------------------------'); @@ -187,7 +187,7 @@ export default function Home() { console.log('---------------------------'); }} > - {({ getRootProps, acceptedFile, ProgressBar }: any) => ( + {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps }: any) => ( <>
{acceptedFile && acceptedFile.name}
+
)} -
*/} -
+
+ {/*
{ console.log('---------------------------'); @@ -311,7 +323,7 @@ export default function Home() { )} -
+
*/} ); } From 252536babea1482200bdaf187f53dcd6c7b7eb80 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 6 Jan 2022 23:57:27 +0700 Subject: [PATCH 111/140] Update Basic Upload --- README.md | 155 ++++++++------------ supports/create-next-app/pages/index.tsx | 172 +++++++++++------------ 2 files changed, 144 insertions(+), 183 deletions(-) diff --git a/README.md b/README.md index 3dadebf..9a2cfa3 100644 --- a/README.md +++ b/README.md @@ -66,108 +66,69 @@ FAQ: ![basic-upload](https://react-papaparse.github.io/static/images/csvreader1.png) ```javascript -import React, { Component } from 'react' +import React, { CSSProperties } from 'react'; -import { CSVReader } from 'react-papaparse' - -const buttonRef = React.createRef() - -export default class CSVReader extends Component { - handleOpenDialog = (e) => { - // Note that the ref is set async, so it might be null at some point - if (buttonRef.current) { - buttonRef.current.open(e) - } - } - - handleOnFileLoad = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } - - handleOnError = (err, file, inputElem, reason) => { - console.log(err) - } +import { useCSVReader } from 'react-papaparse'; - handleOnRemoveFile = (data) => { - console.log('---------------------------') - console.log(data) - console.log('---------------------------') - } +const styles = { + csvReader: { + display: 'flex', + flexDirection: 'row', + marginBottom: 10, + } as CSSProperties, + browseFile: { + width: '20%', + } as CSSProperties, + acceptedFile: { + border: '1px solid #ccc', + height: 45, + lineHeight: 2.5, + paddingLeft: 10, + width: '80%', + } as CSSProperties, + remove: { + borderRadius: 0, + padding: '0 20px', + } as CSSProperties, + progressBarBackgroundColor: { + backgroundColor: 'red', + } as CSSProperties, +}; - handleRemoveFile = (e) => { - // Note that the ref is set async, so it might be null at some point - if (buttonRef.current) { - buttonRef.current.removeFile(e) - } - } +export default function CSVReader() { + const { CSVReader } = useCSVReader(); - render() { - return ( - - {({ file }) => ( - - )} - - ) - } + Browse file + +
+ {acceptedFile && acceptedFile.name} +
+ +
+ + + )} + } ``` diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 2234013..2828bbb 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -37,74 +37,96 @@ export default function Home() { const { CSVReader } = useCSVReader(); const [zoneHover, setZoneHover] = useState(false); const [removeHoverColor, setRemoveHoverColor] = useState(DEFAULT_REMOVE_HOVER_COLOR); + // const styles = { + // zone: { + // alignItems: 'center', + // border: `2px dashed ${GREY}`, + // borderRadius: 20, + // display: 'flex', + // flexDirection: 'column', + // height: '100%', + // justifyContent: 'center', + // padding: 20, + // } as CSSProperties, + // file: { + // background: 'linear-gradient(to bottom, #EEE, #DDD)', + // borderRadius: 20, + // display: 'flex', + // height: 120, + // width: 120, + // position: 'relative', + // zIndex: 10, + // flexDirection: 'column', + // justifyContent: 'center', + // } as CSSProperties, + // info: { + // alignItems: 'center', + // display: 'flex', + // flexDirection: 'column', + // paddingLeft: 10, + // paddingRight: 10, + // } as CSSProperties, + // size: { + // backgroundColor: GREY_LIGHT, + // borderRadius: 3, + // marginBottom: '0.5em', + // justifyContent: 'center', + // display: 'flex', + // } as CSSProperties, + // name: { + // backgroundColor: GREY_LIGHT, + // borderRadius: 3, + // fontSize: 12, + // marginBottom: '0.5em', + // } as CSSProperties, + // progressBar: { + // bottom: 14, + // position: 'absolute', + // width: '100%', + // paddingLeft: 10, + // paddingRight: 10, + // } as CSSProperties, + // zoneHover: { + // borderColor: GREY_DIM, + // } as CSSProperties, + // default: { + // borderColor: GREY, + // } as CSSProperties, + // remove: { + // height: 23, + // position: 'absolute', + // right: 6, + // top: 6, + // width: 23, + // } as CSSProperties, + // // defaultCursor: { + // // cursor: 'default', + // // } as CSSProperties, + // // pointerCursor: { + // // cursor: 'pointer', + // // } as CSSProperties, + // }; + const styles = { - zone: { - alignItems: 'center', - border: `2px dashed ${GREY}`, - borderRadius: 20, + csvReader: { display: 'flex', - flexDirection: 'column', - height: '100%', - justifyContent: 'center', - padding: 20, + flexDirection: 'row', + marginBottom: 10, } as CSSProperties, - file: { - background: 'linear-gradient(to bottom, #EEE, #DDD)', - borderRadius: 20, - display: 'flex', - height: 120, - width: 120, - position: 'relative', - zIndex: 10, - flexDirection: 'column', - justifyContent: 'center', + browseFile: { + width: '20%', } as CSSProperties, - info: { - alignItems: 'center', - display: 'flex', - flexDirection: 'column', + acceptedFile: { + border: '1px solid #ccc', + height: 45, + lineHeight: 2.5, paddingLeft: 10, - paddingRight: 10, - } as CSSProperties, - size: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - marginBottom: '0.5em', - justifyContent: 'center', - display: 'flex', - } as CSSProperties, - name: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - fontSize: 12, - marginBottom: '0.5em', - } as CSSProperties, - progressBar: { - bottom: 14, - position: 'absolute', - width: '100%', - paddingLeft: 10, - paddingRight: 10, - } as CSSProperties, - zoneHover: { - borderColor: GREY_DIM, - } as CSSProperties, - default: { - borderColor: GREY, + width: '80%', } as CSSProperties, remove: { - height: 23, - position: 'absolute', - right: 6, - top: 6, - width: 23, + borderRadius: 0, + padding: '0 20px', } as CSSProperties, - // defaultCursor: { - // cursor: 'default', - // } as CSSProperties, - // pointerCursor: { - // cursor: 'pointer', - // } as CSSProperties, }; return ( @@ -189,42 +211,20 @@ export default function Home() { > {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps }: any) => ( <> -
+
-
+
{acceptedFile && acceptedFile.name}
From 97a54a34e06f0bd7b21cf17aee87f89383f6b8c8 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sat, 8 Jan 2022 00:12:17 +0700 Subject: [PATCH 112/140] Fix rendering object in DOM --- src/useCSVReader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index f8ec711..c4ccbc0 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -472,7 +472,7 @@ function useCSVReaderComponent() { onFocus = () => {}, onBlur = () => {}, onDragEnter = () => {}, - refKey = rootRef, + // refKey = rootRef, ...rest } = {}) => ({ onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), @@ -495,7 +495,7 @@ function useCSVReaderComponent() { onBlur: composeKeyboardHandler( composeEventHandlers(onBlur, onBlurCb), // Done ), - [refKey]: rootRef, + // [refKey]: rootRef, ...rest, }), [ From 0b54f16fa4079ea27530d05c5d764993488c746d Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sat, 8 Jan 2022 16:16:55 +0700 Subject: [PATCH 113/140] Update examples --- README.md | 143 ++++--- demo/CSVDownloader1.js | 85 +++-- demo/CSVDownloader2.js | 34 +- demo/CSVReader1.js | 160 +++----- demo/CSVReader2.js | 178 +++++++-- demo/CSVReader3.js | 185 +++++++-- demo/CSVReader4.js | 185 +++++++-- demo/JsonToCSV.js | 69 ++-- demo/ReadRemoteFile.js | 23 +- demo/ReadString.js | 15 +- docs/src/components/screens/indexes/Navbar.js | 4 +- src/useCSVReader.tsx | 36 +- supports/create-next-app/pages/index.tsx | 357 ++---------------- 13 files changed, 805 insertions(+), 669 deletions(-) diff --git a/README.md b/README.md index 9a2cfa3..0d5f1a6 100644 --- a/README.md +++ b/README.md @@ -98,37 +98,39 @@ const styles = { export default function CSVReader() { const { CSVReader } = useCSVReader(); - { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - }} - > - {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps }: any) => ( - <> -
- -
- {acceptedFile && acceptedFile.name} + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + }} + > + {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps }: any) => ( + <> +
+ +
+ {acceptedFile && acceptedFile.name} +
+
- -
- - - )} - + + + )} + + ); } ``` @@ -138,12 +140,19 @@ export default function CSVReader() { ```javascript import React, { useState, CSSProperties } from 'react'; -import { useCSVReader } from 'react-papaparse'; +import { + useCSVReader, + lightenDarkenColor, + formatFileSize, +} from 'react-papaparse'; const GREY = '#CCC'; const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; -const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(DEFAULT_REMOVE_HOVER_COLOR, 40) +const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( + DEFAULT_REMOVE_HOVER_COLOR, + 40 +); const GREY_DIM = '#686868'; const styles = { @@ -213,7 +222,9 @@ const styles = { export default function CSVReader() { const { CSVReader } = useCSVReader(); const [zoneHover, setZoneHover] = useState(false); - const [removeHoverColor, setRemoveHoverColor] = useState(DEFAULT_REMOVE_HOVER_COLOR); + const [removeHoverColor, setRemoveHoverColor] = useState( + DEFAULT_REMOVE_HOVER_COLOR + ); return ( - {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( + {({ + getRootProps, + acceptedFile, + ProgressBar, + getRemoveFileProps, + Remove, + }: any) => ( <>
- {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( + {({ + getRootProps, + acceptedFile, + ProgressBar, + getRemoveFileProps, + Remove, + }: any) => ( <>
- {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( + {({ + getRootProps, + acceptedFile, + ProgressBar, + getRemoveFileProps, + Remove, + }: any) => ( <>
- Download - - ); - } +import { useCSVDownloader } from 'react-papaparse'; + +export default function CSVDownloader() { + const { CSVDownloader, Type } = useCSVDownloader(); + + return ( + + Download + + ); } diff --git a/demo/CSVDownloader2.js b/demo/CSVDownloader2.js index c3a8199..c3feddd 100644 --- a/demo/CSVDownloader2.js +++ b/demo/CSVDownloader2.js @@ -1,20 +1,22 @@ -import React, { Component } from 'react'; -import { CSVDownloader } from 'react-papaparse'; +import React from 'react'; -export default class CSVDownloader2 extends Component { - render() { - return ( - - Download - - ); - } + > + Download + + ); } diff --git a/demo/CSVReader1.js b/demo/CSVReader1.js index 8cb6233..e0b8c41 100644 --- a/demo/CSVReader1.js +++ b/demo/CSVReader1.js @@ -1,107 +1,67 @@ -import React, { Component } from 'react'; -import { CSVReader } from 'react-papaparse'; -const buttonRef = React.createRef(); +import React, { CSSProperties } from 'react'; -export default class CSVReader1 extends Component { - handleOpenDialog = (e) => { - // Note that the ref is set async, so it might be null at some point - if (buttonRef.current) { - buttonRef.current.open(e); - } - }; +import { useCSVReader } from 'react-papaparse'; - handleOnFileLoad = (data) => { - console.log('---------------------------'); - console.log(data); - console.log('---------------------------'); - }; +const styles = { + csvReader: { + display: 'flex', + flexDirection: 'row', + marginBottom: 10, + } as CSSProperties, + browseFile: { + width: '20%', + } as CSSProperties, + acceptedFile: { + border: '1px solid #ccc', + height: 45, + lineHeight: 2.5, + paddingLeft: 10, + width: '80%', + } as CSSProperties, + remove: { + borderRadius: 0, + padding: '0 20px', + } as CSSProperties, + progressBarBackgroundColor: { + backgroundColor: 'red', + } as CSSProperties, +}; - handleOnError = (err, file, inputElem, reason) => { - console.log('---------------------------'); - console.log(err); - console.log('---------------------------'); - }; +export default function CSVReader() { + const { CSVReader } = useCSVReader(); - handleOnRemoveFile = (data) => { - console.log('---------------------------'); - console.log(data); - console.log('---------------------------'); - }; - - handleRemoveFile = (e) => { - // Note that the ref is set async, so it might be null at some point - if (buttonRef.current) { - buttonRef.current.removeFile(e); - } - }; - - render() { - return ( - <> -
Basic Upload
- - {({ file }) => ( - - )} - - - ); - } + Remove + +
+ + + )} + + ); } diff --git a/demo/CSVReader2.js b/demo/CSVReader2.js index 3a79458..954c0dd 100644 --- a/demo/CSVReader2.js +++ b/demo/CSVReader2.js @@ -1,31 +1,157 @@ -import React, { Component } from 'react'; -import { CSVReader } from 'react-papaparse'; +import React, { useState, CSSProperties } from 'react'; +import { + useCSVReader, + lightenDarkenColor, + formatFileSize, +} from 'react-papaparse'; -export default class CSVReader2 extends Component { - handleOnDrop = (data) => { - console.log('---------------------------'); - console.log(data); - console.log('---------------------------'); - }; +const GREY = '#CCC'; +const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; +const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; +const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( + DEFAULT_REMOVE_HOVER_COLOR, + 40 +); +const GREY_DIM = '#686868'; - handleOnError = (err, file, inputElem, reason) => { - console.log(err); - }; +const styles = { + zone: { + alignItems: 'center', + border: `2px dashed ${GREY}`, + borderRadius: 20, + display: 'flex', + flexDirection: 'column', + height: '100%', + justifyContent: 'center', + padding: 20, + } as CSSProperties, + file: { + background: 'linear-gradient(to bottom, #EEE, #DDD)', + borderRadius: 20, + display: 'flex', + height: 120, + width: 120, + position: 'relative', + zIndex: 10, + flexDirection: 'column', + justifyContent: 'center', + } as CSSProperties, + info: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + size: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + marginBottom: '0.5em', + justifyContent: 'center', + display: 'flex', + } as CSSProperties, + name: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + fontSize: 12, + marginBottom: '0.5em', + } as CSSProperties, + progressBar: { + bottom: 14, + position: 'absolute', + width: '100%', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + zoneHover: { + borderColor: GREY_DIM, + } as CSSProperties, + default: { + borderColor: GREY, + } as CSSProperties, + remove: { + height: 23, + position: 'absolute', + right: 6, + top: 6, + width: 23, + } as CSSProperties, +}; - handleOnRemoveFile = (data) => { - console.log('---------------------------'); - console.log(data); - console.log('---------------------------'); - }; +export default function CSVReader() { + const { CSVReader } = useCSVReader(); + const [zoneHover, setZoneHover] = useState(false); + const [removeHoverColor, setRemoveHoverColor] = useState( + DEFAULT_REMOVE_HOVER_COLOR + ); - render() { - return ( - <> -
Click and Drag Upload
- - Drop CSV file here or click to upload. - - - ); - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + setZoneHover(false); + }} + onDragOver={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(true); + }} + onDragLeave={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(false); + }} + > + {({ + getRootProps, + acceptedFile, + ProgressBar, + getRemoveFileProps, + Remove, + }: any) => ( + <> +
+ {acceptedFile ? ( + <> +
+
+ + {formatFileSize(acceptedFile.size)} + + {acceptedFile.name} +
+
+ +
+
{ + event.preventDefault(); + setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); + }} + onMouseOut={(event) => { + event.preventDefault(); + setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); + }} + > + +
+
+ + ) : ( + 'Drop CSV file here or click to upload' + )} +
+ + )} +
+ ); } diff --git a/demo/CSVReader3.js b/demo/CSVReader3.js index 1d7a398..29aa439 100644 --- a/demo/CSVReader3.js +++ b/demo/CSVReader3.js @@ -1,37 +1,158 @@ -import React, { Component } from 'react'; -import { CSVReader } from 'react-papaparse'; +import React, { useState, CSSProperties } from 'react'; +import { + useCSVReader, + lightenDarkenColor, + formatFileSize, +} from 'react-papaparse'; -export default class CSVReader3 extends Component { - handleOnDrop = (data) => { - console.log('---------------------------'); - console.log(data); - console.log('---------------------------'); - }; +const GREY = '#CCC'; +const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; +const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; +const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( + DEFAULT_REMOVE_HOVER_COLOR, + 40 +); +const GREY_DIM = '#686868'; - handleOnError = (err, file, inputElem, reason) => { - console.log(err); - }; +const styles = { + zone: { + alignItems: 'center', + border: `2px dashed ${GREY}`, + borderRadius: 20, + display: 'flex', + flexDirection: 'column', + height: '100%', + justifyContent: 'center', + padding: 20, + } as CSSProperties, + file: { + background: 'linear-gradient(to bottom, #EEE, #DDD)', + borderRadius: 20, + display: 'flex', + height: 120, + width: 120, + position: 'relative', + zIndex: 10, + flexDirection: 'column', + justifyContent: 'center', + } as CSSProperties, + info: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + size: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + marginBottom: '0.5em', + justifyContent: 'center', + display: 'flex', + } as CSSProperties, + name: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + fontSize: 12, + marginBottom: '0.5em', + } as CSSProperties, + progressBar: { + bottom: 14, + position: 'absolute', + width: '100%', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + zoneHover: { + borderColor: GREY_DIM, + } as CSSProperties, + default: { + borderColor: GREY, + } as CSSProperties, + remove: { + height: 23, + position: 'absolute', + right: 6, + top: 6, + width: 23, + } as CSSProperties, +}; - handleOnRemoveFile = (data) => { - console.log('---------------------------'); - console.log(data); - console.log('---------------------------'); - }; +export default function CSVReader() { + const { CSVReader } = useCSVReader(); + const [zoneHover, setZoneHover] = useState(false); + const [removeHoverColor, setRemoveHoverColor] = useState( + DEFAULT_REMOVE_HOVER_COLOR + ); - render() { - return ( - <> -
Drag ( No Click ) Upload
- - Drop CSV file here to upload. - - - ); - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + setZoneHover(false); + }} + onDragOver={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(true); + }} + onDragLeave={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(false); + }} + noClick + > + {({ + getRootProps, + acceptedFile, + ProgressBar, + getRemoveFileProps, + Remove, + }: any) => ( + <> +
+ {acceptedFile ? ( + <> +
+
+ + {formatFileSize(acceptedFile.size)} + + {acceptedFile.name} +
+
+ +
+
{ + event.preventDefault(); + setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); + }} + onMouseOut={(event) => { + event.preventDefault(); + setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); + }} + > + +
+
+ + ) : ( + 'Drop CSV file here to upload' + )} +
+ + )} +
+ ); } diff --git a/demo/CSVReader4.js b/demo/CSVReader4.js index ee4ee7d..d7ceafe 100644 --- a/demo/CSVReader4.js +++ b/demo/CSVReader4.js @@ -1,37 +1,158 @@ -import React, { Component } from 'react'; -import { CSVReader } from 'react-papaparse'; +import React, { useState, CSSProperties } from 'react'; +import { + useCSVReader, + lightenDarkenColor, + formatFileSize, +} from 'react-papaparse'; -export default class CSVReader4 extends Component { - handleOnDrop = (data) => { - console.log('---------------------------'); - console.log(data); - console.log('---------------------------'); - }; +const GREY = '#CCC'; +const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; +const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; +const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( + DEFAULT_REMOVE_HOVER_COLOR, + 40 +); +const GREY_DIM = '#686868'; - handleOnError = (err, file, inputElem, reason) => { - console.log(err); - }; +const styles = { + zone: { + alignItems: 'center', + border: `2px dashed ${GREY}`, + borderRadius: 20, + display: 'flex', + flexDirection: 'column', + height: '100%', + justifyContent: 'center', + padding: 20, + } as CSSProperties, + file: { + background: 'linear-gradient(to bottom, #EEE, #DDD)', + borderRadius: 20, + display: 'flex', + height: 120, + width: 120, + position: 'relative', + zIndex: 10, + flexDirection: 'column', + justifyContent: 'center', + } as CSSProperties, + info: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + size: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + marginBottom: '0.5em', + justifyContent: 'center', + display: 'flex', + } as CSSProperties, + name: { + backgroundColor: GREY_LIGHT, + borderRadius: 3, + fontSize: 12, + marginBottom: '0.5em', + } as CSSProperties, + progressBar: { + bottom: 14, + position: 'absolute', + width: '100%', + paddingLeft: 10, + paddingRight: 10, + } as CSSProperties, + zoneHover: { + borderColor: GREY_DIM, + } as CSSProperties, + default: { + borderColor: GREY, + } as CSSProperties, + remove: { + height: 23, + position: 'absolute', + right: 6, + top: 6, + width: 23, + } as CSSProperties, +}; - handleOnRemoveFile = (data) => { - console.log('---------------------------'); - console.log(data); - console.log('---------------------------'); - }; +export default function CSVReader() { + const { CSVReader } = useCSVReader(); + const [zoneHover, setZoneHover] = useState(false); + const [removeHoverColor, setRemoveHoverColor] = useState( + DEFAULT_REMOVE_HOVER_COLOR + ); - render() { - return ( - <> -
Click ( No Drag ) Upload
- - Click to upload. - - - ); - } + return ( + { + console.log('---------------------------'); + console.log(results); + console.log('---------------------------'); + setZoneHover(false); + }} + onDragOver={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(true); + }} + onDragLeave={(event: DragEvent) => { + event.preventDefault(); + setZoneHover(false); + }} + noDrag + > + {({ + getRootProps, + acceptedFile, + ProgressBar, + getRemoveFileProps, + Remove, + }: any) => ( + <> +
+ {acceptedFile ? ( + <> +
+
+ + {formatFileSize(acceptedFile.size)} + + {acceptedFile.name} +
+
+ +
+
{ + event.preventDefault(); + setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); + }} + onMouseOut={(event) => { + event.preventDefault(); + setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); + }} + > + +
+
+ + ) : ( + 'Click to upload' + )} +
+ + )} +
+ ); } diff --git a/demo/JsonToCSV.js b/demo/JsonToCSV.js index f944cbc..5ed8256 100644 --- a/demo/JsonToCSV.js +++ b/demo/JsonToCSV.js @@ -1,43 +1,42 @@ -import React, { Component } from 'react'; -import { jsonToCSV } from 'react-papaparse'; +import React from 'react'; -export default class JsonToCSV extends Component { - handleClick = () => { - const jsonData = `[ - { - "Column 1": "1-1", - "Column 2": "1-2", - "Column 3": "1-3", - "Column 4": "1-4" - }, - { - "Column 1": "2-1", - "Column 2": "2-2", - "Column 3": "2-3", - "Column 4": "2-4" - }, - { - "Column 1": "3-1", - "Column 2": "3-2", - "Column 3": "3-3", - "Column 4": "3-4" - }, - { - "Column 1": 4, - "Column 2": 5, - "Column 3": 6, - "Column 4": 7 - } -]`; +import { usePapaParse } from 'react-papaparse'; - const results = jsonToCSV(jsonData); +export default function JsonToCSV() { + const { jsonToCSV } = usePapaParse(); + const handleJsonToCSV = () => { + const jsonData = `[ + { + "Column 1": "1-1", + "Column 2": "1-2", + "Column 3": "1-3", + "Column 4": "1-4" + }, + { + "Column 1": "2-1", + "Column 2": "2-2", + "Column 3": "2-3", + "Column 4": "2-4" + }, + { + "Column 1": "3-1", + "Column 2": "3-2", + "Column 3": "3-3", + "Column 4": "3-4" + }, + { + "Column 1": 4, + "Column 2": 5, + "Column 3": 6, + "Column 4": 7 + } + ]`; + const results = jsonToCSV(jsonData) console.log('---------------------------'); - console.log(results); + console.log('Results:', results) console.log('---------------------------'); }; - render() { - return ; - } + return (); } diff --git a/demo/ReadRemoteFile.js b/demo/ReadRemoteFile.js index 3d2f6c1..93d9ee0 100644 --- a/demo/ReadRemoteFile.js +++ b/demo/ReadRemoteFile.js @@ -1,16 +1,19 @@ -import React, { Component } from 'react'; -import { readRemoteFile } from 'react-papaparse'; +import React from 'react'; -export default class ReadRemoteFile extends Component { - handleClick = () => { - readRemoteFile('http://example.com/file.csv', { +import { usePapaParse } from 'react-papaparse'; + +export default function ReadRemoteFile() { + const { readRemoteFile } = usePapaParse(); + + const handleReadRemoteFile = () => { + readRemoteFile(url, { complete: (results) => { - console.log('Results:', results); - }, + console.log('---------------------------'); + console.log('Results:', results) + console.log('---------------------------'); + } }); }; - render() { - return ; - } + return (); } diff --git a/demo/ReadString.js b/demo/ReadString.js index 5583e56..feeda16 100644 --- a/demo/ReadString.js +++ b/demo/ReadString.js @@ -1,8 +1,11 @@ -import React, { Component } from 'react'; -import { readString } from 'react-papaparse'; +import React from 'react'; -export default class ReadString extends Component { - handleClick = () => { +import { usePapaParse } from 'react-papaparse'; + +export default function ReadString() { + const { readString } = usePapaParse(); + + const handleReadString = () => { const csvString = `Column 1,Column 2,Column 3,Column 4 1-1,1-2,1-3,1-4 2-1,2-2,2-3,2-4 @@ -19,7 +22,5 @@ export default class ReadString extends Component { }); }; - render() { - return ; - } + return (); } diff --git a/docs/src/components/screens/indexes/Navbar.js b/docs/src/components/screens/indexes/Navbar.js index 54680ea..3440e93 100644 --- a/docs/src/components/screens/indexes/Navbar.js +++ b/docs/src/components/screens/indexes/Navbar.js @@ -17,7 +17,7 @@ const Navbar = () => {
@@ -32,7 +32,7 @@ const Navbar = () => {
Version
-
3
+
4
diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index c4ccbc0..42cf92f 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -90,6 +90,7 @@ function useCSVReaderComponent() { displayProgressBar, progressBarPercentage, draggedFiles, + isFileDialogActive, } = state; const onDocumentDrop = (event: DragEvent) => { @@ -115,7 +116,7 @@ function useCSVReaderComponent() { }; }, [rootRef, preventDropOnDocument]); - // ============== GLOBAL ============== + // == GLOBAL == const composeHandler = (fn: any) => { return disabled ? null : fn; }; @@ -185,6 +186,29 @@ function useCSVReaderComponent() { } }, [dispatch]); + // Update file dialog active state when the window is focused on + const onWindowFocus = () => { + // Execute the timeout only if the file dialog is opened in the browser + if (isFileDialogActive) { + setTimeout(() => { + if (inputRef.current) { + const { files } = inputRef.current; + + if (!files.length) { + dispatch({ type: 'closeDialog' }); + } + } + }, 300); + } + }; + + useEffect(() => { + window.addEventListener('focus', onWindowFocus, false); + return () => { + window.removeEventListener('focus', onWindowFocus, false); + } + }, [inputRef, isFileDialogActive]); + // Cb to open the file dialog when click occurs on the dropzone const onClickCb = useCallback(() => { if (noClick) { @@ -349,9 +373,9 @@ function useCSVReaderComponent() { const onInputElementClick = useCallback((event) => { stopPropagation(event); }, []); - // ==================================== + // ============ - // ============== BUTTON | DROP ============== + // == BUTTON | DROP == const composeKeyboardHandler = (fn: any) => { return noKeyboard ? null : composeHandler(fn); }; @@ -513,9 +537,9 @@ function useCSVReaderComponent() { disabled, ], ); - // ================================== + // =================== - // ============== INPUT ============== + // == INPUT == const getInputProps = useMemo( () => ({ @@ -545,7 +569,7 @@ function useCSVReaderComponent() { }, [inputRef, accept, onDropCb, disabled], ); - // =================================== + // =========== const removeFileProgrammaticallyCb = useCallback((event: Event) => { inputRef.current.value = ''; diff --git a/supports/create-next-app/pages/index.tsx b/supports/create-next-app/pages/index.tsx index 2828bbb..5ed8256 100644 --- a/supports/create-next-app/pages/index.tsx +++ b/supports/create-next-app/pages/index.tsx @@ -1,329 +1,42 @@ -import React, { useState, CSSProperties } from 'react'; -import { - usePapaParse, - useCSVDownloader, - useCSVReader, - formatFileSize, - lightenDarkenColor, -} from 'react-papaparse'; +import React from 'react'; -const GREY = '#CCC'; -const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; -const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; -const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor(DEFAULT_REMOVE_HOVER_COLOR, 40) -const GREY_DIM = '#686868'; +import { usePapaParse } from 'react-papaparse'; -export default function Home() { - const { CSVDownloader, Type } = useCSVDownloader(); +export default function JsonToCSV() { + const { jsonToCSV } = usePapaParse(); - const { readString } = usePapaParse(); - const handleReadString = () => { - const csvString = `Column 1,Column 2,Column 3,Column 4 -1-1,1-2,1-3,1-4 -2-1,2-2,2-3,2-4 -3-1,3-2,3-3,3-4 -4,5,6,7`; - - readString(csvString, { - worker: true, - complete: (results) => { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); + const handleJsonToCSV = () => { + const jsonData = `[ + { + "Column 1": "1-1", + "Column 2": "1-2", + "Column 3": "1-3", + "Column 4": "1-4" }, - }); - }; - - const { CSVReader } = useCSVReader(); - const [zoneHover, setZoneHover] = useState(false); - const [removeHoverColor, setRemoveHoverColor] = useState(DEFAULT_REMOVE_HOVER_COLOR); - // const styles = { - // zone: { - // alignItems: 'center', - // border: `2px dashed ${GREY}`, - // borderRadius: 20, - // display: 'flex', - // flexDirection: 'column', - // height: '100%', - // justifyContent: 'center', - // padding: 20, - // } as CSSProperties, - // file: { - // background: 'linear-gradient(to bottom, #EEE, #DDD)', - // borderRadius: 20, - // display: 'flex', - // height: 120, - // width: 120, - // position: 'relative', - // zIndex: 10, - // flexDirection: 'column', - // justifyContent: 'center', - // } as CSSProperties, - // info: { - // alignItems: 'center', - // display: 'flex', - // flexDirection: 'column', - // paddingLeft: 10, - // paddingRight: 10, - // } as CSSProperties, - // size: { - // backgroundColor: GREY_LIGHT, - // borderRadius: 3, - // marginBottom: '0.5em', - // justifyContent: 'center', - // display: 'flex', - // } as CSSProperties, - // name: { - // backgroundColor: GREY_LIGHT, - // borderRadius: 3, - // fontSize: 12, - // marginBottom: '0.5em', - // } as CSSProperties, - // progressBar: { - // bottom: 14, - // position: 'absolute', - // width: '100%', - // paddingLeft: 10, - // paddingRight: 10, - // } as CSSProperties, - // zoneHover: { - // borderColor: GREY_DIM, - // } as CSSProperties, - // default: { - // borderColor: GREY, - // } as CSSProperties, - // remove: { - // height: 23, - // position: 'absolute', - // right: 6, - // top: 6, - // width: 23, - // } as CSSProperties, - // // defaultCursor: { - // // cursor: 'default', - // // } as CSSProperties, - // // pointerCursor: { - // // cursor: 'pointer', - // // } as CSSProperties, - // }; - - const styles = { - csvReader: { - display: 'flex', - flexDirection: 'row', - marginBottom: 10, - } as CSSProperties, - browseFile: { - width: '20%', - } as CSSProperties, - acceptedFile: { - border: '1px solid #ccc', - height: 45, - lineHeight: 2.5, - paddingLeft: 10, - width: '80%', - } as CSSProperties, - remove: { - borderRadius: 0, - padding: '0 20px', - } as CSSProperties, + { + "Column 1": "2-1", + "Column 2": "2-2", + "Column 3": "2-3", + "Column 4": "2-4" + }, + { + "Column 1": "3-1", + "Column 2": "3-2", + "Column 3": "3-3", + "Column 4": "3-4" + }, + { + "Column 1": 4, + "Column 2": 5, + "Column 3": 6, + "Column 4": 7 + } + ]`; + const results = jsonToCSV(jsonData) + console.log('---------------------------'); + console.log('Results:', results) + console.log('---------------------------'); }; - return ( - <> -
- {/* - Download ( Button ) - */} - {/* - Download ( Link ) - */} - {/* { - return [ - { - "Column 1": "1-1", - "Column 2": "1-2", - "Column 3": "1-3", - "Column 4": "1-4", - } - ]} - } - type={Type.Button} - > - Download ( Data as a Function/Callback ) - */} -
- {/*
- -
*/} -
- { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - }} - > - {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps }: any) => ( - <> -
- -
- {acceptedFile && acceptedFile.name} -
- -
- - - )} -
-
- {/*
- { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - setZoneHover(false); - }} - onDragOver={(event: DragEvent) => { - event.preventDefault(); - setZoneHover(true); - }} - onDragLeave={(event: DragEvent) => { - event.preventDefault(); - setZoneHover(false); - }} - // noDrag - > - {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps, Remove }: any) => ( - <> -
- {acceptedFile ? ( - <> -
-
- - {formatFileSize(acceptedFile.size)} - - {acceptedFile.name} -
-
- -
-
{ - event.preventDefault(); - setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); - }} - onMouseOut={(event) => { - event.preventDefault(); - setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); - }} - > - -
-
- - ) : ( - 'Drop CSV file here or click to upload.' - - //
- //
- // - // 1 KB - // - // normal.csv - //
- //
- // - //
- //
{ - // event.preventDefault(); - // setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); - // }} - // onMouseOut={(event) => { - // event.preventDefault(); - // setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); - // }} - // > - // - //
- //
- )} -
- - )} -
-
*/} - - ); + return (); } From 453f0460159e642bec6a65452c371959c4a42d96 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sat, 8 Jan 2022 16:32:48 +0700 Subject: [PATCH 114/140] Format files --- demo/CSVDownloader1.js | 48 -- demo/CSVDownloader1.tsx | 46 ++ .../{CSVDownloader2.js => CSVDownloader2.tsx} | 0 demo/{CSVReader1.js => CSVReader1.tsx} | 21 +- demo/{CSVReader2.js => CSVReader2.tsx} | 6 +- demo/{CSVReader3.js => CSVReader3.tsx} | 6 +- demo/{CSVReader4.js => CSVReader4.tsx} | 6 +- demo/{JsonToCSV.js => JsonToCSV.tsx} | 6 +- .../{ReadRemoteFile.js => ReadRemoteFile.tsx} | 6 +- demo/{ReadString.js => ReadString.tsx} | 2 +- package.json | 2 +- src/CSVDownloader.tsx | 96 --- src/CSVReader.tsx | 588 ------------------ src/RemoveIcon.tsx | 21 - src/react-papaparse.ts | 13 +- src/useCSVReader.tsx | 3 +- 16 files changed, 74 insertions(+), 796 deletions(-) delete mode 100644 demo/CSVDownloader1.js create mode 100644 demo/CSVDownloader1.tsx rename demo/{CSVDownloader2.js => CSVDownloader2.tsx} (100%) rename demo/{CSVReader1.js => CSVReader1.tsx} (76%) rename demo/{CSVReader2.js => CSVReader2.tsx} (97%) rename demo/{CSVReader3.js => CSVReader3.tsx} (97%) rename demo/{CSVReader4.js => CSVReader4.tsx} (97%) rename demo/{JsonToCSV.js => JsonToCSV.tsx} (84%) rename demo/{ReadRemoteFile.js => ReadRemoteFile.tsx} (74%) rename demo/{ReadString.js => ReadString.tsx} (87%) delete mode 100644 src/CSVDownloader.tsx delete mode 100644 src/CSVReader.tsx delete mode 100644 src/RemoveIcon.tsx diff --git a/demo/CSVDownloader1.js b/demo/CSVDownloader1.js deleted file mode 100644 index a843bc5..0000000 --- a/demo/CSVDownloader1.js +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; - -import { useCSVDownloader } from 'react-papaparse'; - -export default function CSVDownloader() { - const { CSVDownloader, Type } = useCSVDownloader(); - - return ( - - Download - - ); -} diff --git a/demo/CSVDownloader1.tsx b/demo/CSVDownloader1.tsx new file mode 100644 index 0000000..ea0dee0 --- /dev/null +++ b/demo/CSVDownloader1.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import { useCSVDownloader } from 'react-papaparse'; + +export default function CSVDownloader() { + const { CSVDownloader, Type } = useCSVDownloader(); + + return ( + + Download + + ); +} diff --git a/demo/CSVDownloader2.js b/demo/CSVDownloader2.tsx similarity index 100% rename from demo/CSVDownloader2.js rename to demo/CSVDownloader2.tsx diff --git a/demo/CSVReader1.js b/demo/CSVReader1.tsx similarity index 76% rename from demo/CSVReader1.js rename to demo/CSVReader1.tsx index e0b8c41..bdcb84b 100644 --- a/demo/CSVReader1.js +++ b/demo/CSVReader1.tsx @@ -1,4 +1,3 @@ - import React, { CSSProperties } from 'react'; import { useCSVReader } from 'react-papaparse'; @@ -33,29 +32,27 @@ export default function CSVReader() { return ( { + onUploadAccepted={(results: any) => { console.log('---------------------------'); console.log(results); console.log('---------------------------'); }} > - {({ getRootProps, acceptedFile, ProgressBar, getRemoveFileProps }: any) => ( + {({ + getRootProps, + acceptedFile, + ProgressBar, + getRemoveFileProps, + }: any) => ( <>
-
{acceptedFile && acceptedFile.name}
-
diff --git a/demo/CSVReader2.js b/demo/CSVReader2.tsx similarity index 97% rename from demo/CSVReader2.js rename to demo/CSVReader2.tsx index 954c0dd..05ad48c 100644 --- a/demo/CSVReader2.js +++ b/demo/CSVReader2.tsx @@ -10,7 +10,7 @@ const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( DEFAULT_REMOVE_HOVER_COLOR, - 40 + 40, ); const GREY_DIM = '#686868'; @@ -82,7 +82,7 @@ export default function CSVReader() { const { CSVReader } = useCSVReader(); const [zoneHover, setZoneHover] = useState(false); const [removeHoverColor, setRemoveHoverColor] = useState( - DEFAULT_REMOVE_HOVER_COLOR + DEFAULT_REMOVE_HOVER_COLOR, ); return ( @@ -115,7 +115,7 @@ export default function CSVReader() { style={Object.assign( {}, styles.zone, - zoneHover && styles.zoneHover + zoneHover && styles.zoneHover, )} > {acceptedFile ? ( diff --git a/demo/CSVReader3.js b/demo/CSVReader3.tsx similarity index 97% rename from demo/CSVReader3.js rename to demo/CSVReader3.tsx index 29aa439..72c75d4 100644 --- a/demo/CSVReader3.js +++ b/demo/CSVReader3.tsx @@ -10,7 +10,7 @@ const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( DEFAULT_REMOVE_HOVER_COLOR, - 40 + 40, ); const GREY_DIM = '#686868'; @@ -82,7 +82,7 @@ export default function CSVReader() { const { CSVReader } = useCSVReader(); const [zoneHover, setZoneHover] = useState(false); const [removeHoverColor, setRemoveHoverColor] = useState( - DEFAULT_REMOVE_HOVER_COLOR + DEFAULT_REMOVE_HOVER_COLOR, ); return ( @@ -116,7 +116,7 @@ export default function CSVReader() { style={Object.assign( {}, styles.zone, - zoneHover && styles.zoneHover + zoneHover && styles.zoneHover, )} > {acceptedFile ? ( diff --git a/demo/CSVReader4.js b/demo/CSVReader4.tsx similarity index 97% rename from demo/CSVReader4.js rename to demo/CSVReader4.tsx index d7ceafe..a7d77d7 100644 --- a/demo/CSVReader4.js +++ b/demo/CSVReader4.tsx @@ -10,7 +10,7 @@ const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( DEFAULT_REMOVE_HOVER_COLOR, - 40 + 40, ); const GREY_DIM = '#686868'; @@ -82,7 +82,7 @@ export default function CSVReader() { const { CSVReader } = useCSVReader(); const [zoneHover, setZoneHover] = useState(false); const [removeHoverColor, setRemoveHoverColor] = useState( - DEFAULT_REMOVE_HOVER_COLOR + DEFAULT_REMOVE_HOVER_COLOR, ); return ( @@ -116,7 +116,7 @@ export default function CSVReader() { style={Object.assign( {}, styles.zone, - zoneHover && styles.zoneHover + zoneHover && styles.zoneHover, )} > {acceptedFile ? ( diff --git a/demo/JsonToCSV.js b/demo/JsonToCSV.tsx similarity index 84% rename from demo/JsonToCSV.js rename to demo/JsonToCSV.tsx index 5ed8256..4142af9 100644 --- a/demo/JsonToCSV.js +++ b/demo/JsonToCSV.tsx @@ -32,11 +32,11 @@ export default function JsonToCSV() { "Column 4": 7 } ]`; - const results = jsonToCSV(jsonData) + const results = jsonToCSV(jsonData); console.log('---------------------------'); - console.log('Results:', results) + console.log('Results:', results); console.log('---------------------------'); }; - return (); + return ; } diff --git a/demo/ReadRemoteFile.js b/demo/ReadRemoteFile.tsx similarity index 74% rename from demo/ReadRemoteFile.js rename to demo/ReadRemoteFile.tsx index 93d9ee0..0f24129 100644 --- a/demo/ReadRemoteFile.js +++ b/demo/ReadRemoteFile.tsx @@ -9,11 +9,11 @@ export default function ReadRemoteFile() { readRemoteFile(url, { complete: (results) => { console.log('---------------------------'); - console.log('Results:', results) + console.log('Results:', results); console.log('---------------------------'); - } + }, }); }; - return (); + return ; } diff --git a/demo/ReadString.js b/demo/ReadString.tsx similarity index 87% rename from demo/ReadString.js rename to demo/ReadString.tsx index feeda16..d631062 100644 --- a/demo/ReadString.js +++ b/demo/ReadString.tsx @@ -22,5 +22,5 @@ export default function ReadString() { }); }; - return (); + return ; } diff --git a/package.json b/package.json index 4d95d82..3bdd6ea 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "scripts": { "test": "jest --runInBand", "build": "rollup -c", - "prettier": "prettier --write './src/*.ts' './src/*.tsx' './demo/*js' --config ./.prettierrc", + "prettier": "prettier --write './src/*.ts' './src/*.tsx' './demo/*tsx' --config ./.prettierrc", "lint:check": "eslint ./src --ext .tsx,.ts --report-unused-disable-directives", "bundlesize": "npm run build && bundlesize", "dev": "rollup -c -w", diff --git a/src/CSVDownloader.tsx b/src/CSVDownloader.tsx deleted file mode 100644 index 4e42051..0000000 --- a/src/CSVDownloader.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React from 'react'; -import PapaParse, { UnparseConfig } from 'papaparse'; - -export const LINK_TYPE = 'link'; -export const BUTTON_TYPE = 'button'; - -export interface Props { - children: React.ReactNode; - data: any; - filename: string; - type?: 'link' | 'button'; - style?: any; - className?: string; - bom?: boolean; - config?: UnparseConfig; -} - -export default class CSVDownloader extends React.Component { - static defaultProps: Partial = { - type: LINK_TYPE, - }; - - // https://github.com/mholt/PapaParse/issues/175 - download = ( - data: any, - filename: string, - bom: boolean, - config: UnparseConfig, - ): void => { - const bomCode = bom ? '\ufeff' : ''; - let csvContent = null; - let csvURL = null; - - if (typeof data === 'function') { - data = data(); - } - - if (typeof data === 'object') { - csvContent = PapaParse.unparse(data, config); - } else { - csvContent = data; - } - - const csvData = new Blob([`${bomCode}${csvContent}`], { - type: 'text/csv;charset=utf-8;', - }); - - const navObj: any = window.navigator; - if (navObj.msSaveBlob) { - csvURL = navObj.msSaveBlob(csvData, `${filename}.csv`); - } else { - csvURL = window.URL.createObjectURL(csvData); - } - - const link = document.createElement('a'); - link.href = csvURL as string; - link.setAttribute('download', `${filename}.csv`); - link.click(); - link.remove(); - }; - - render(): React.ReactNode { - const { - children, - data, - filename, - type, - className, - style, - bom = false, - config = {}, - } = this.props; - - if (type === LINK_TYPE) { - return ( - this.download(data, filename, bom, config)} - className={className} - style={style} - > - {children} - - ); - } - - return ( - - ); - } -} diff --git a/src/CSVReader.tsx b/src/CSVReader.tsx deleted file mode 100644 index 24e2a2d..0000000 --- a/src/CSVReader.tsx +++ /dev/null @@ -1,588 +0,0 @@ -import React, { CSSProperties } from 'react'; -import PapaParse, { ParseResult } from 'papaparse'; -import { CustomConfig } from './model'; -import { formatFileSize, lightenDarkenColor } from './utils'; -import RemoveIcon from './RemoveIcon'; -import ProgressBar from './ProgressBar'; - -const GREY = '#CCC'; -const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; -const REMOVE_ICON_DEFAULT_COLOR = '#A01919'; -const GREY_DIM = '#686868'; -// 'text/csv' for MacOS -// '.csv' for Linux -// 'application/vnd.ms-excel' for Window 10 -const DEFAULT_ACCEPT = 'text/csv, .csv, application/vnd.ms-excel'; - -const styles = { - dropArea: { - alignItems: 'center', - borderStyle: 'dashed', - borderWidth: 2, - borderRadius: 20, - borderColor: GREY, - display: 'flex', - flexDirection: 'column', - height: '100%', - justifyContent: 'center', - padding: 20, - } as CSSProperties, - dropAreaDefaultBorderColor: { - borderColor: GREY, - }, - inputFile: { - display: 'none', - } as CSSProperties, - highlight: { - borderColor: GREY_DIM, - }, - unhighlight: { - borderColor: GREY, - } as CSSProperties, - dropFile: { - background: 'linear-gradient(to bottom, #EEE, #DDD)', - borderRadius: 20, - display: 'block', - height: 120, - width: 100, - paddingLeft: 10, - paddingRight: 10, - position: 'relative', - zIndex: 10, - } as CSSProperties, - column: { - alignItems: 'center', - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - } as CSSProperties, - fileSizeInfo: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - lineHeight: 1, - marginBottom: '0.5em', - padding: '0 0.4em', - } as CSSProperties, - fileNameInfo: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - fontSize: 14, - lineHeight: 1, - padding: '0 0.4em', - } as CSSProperties, - defaultCursor: { - cursor: 'default', - } as CSSProperties, - pointerCursor: { - cursor: 'pointer', - } as CSSProperties, - dropFileRemoveButton: { - height: 23, - position: 'absolute', - right: 6, - top: 6, - width: 23, - } as CSSProperties, -}; - -interface Props { - children: any; - onDrop?: (data: Array>, file?: any) => void; - onFileLoad?: (data: Array>, file?: any) => void; - onError?: (err: any, file: any, inputElem: any, reason: any) => void; - config?: CustomConfig; - style?: any; - noClick?: boolean; - noDrag?: boolean; - progressBarColor?: string; - addRemoveButton?: boolean; - onRemoveFile?: (data: null) => void; - noProgressBar?: boolean; - removeButtonColor?: string; - isReset?: boolean; - accept?: string; -} - -interface State { - dropAreaCustom: any; - progressBar: number; - displayProgressBarStatus: string; - file: any; - timeout: any; - files: any; - removeIconColor: string; - isCanceled: boolean; -} - -export default class CSVReader extends React.Component< - Props, - State -> { - static defaultProps: Partial> = { - isReset: false, - }; - - // TODO - // inputFileRef: React.RefObject = React.createRef() - inputFileRef: any = React.createRef(); - dropAreaRef: any = React.createRef(); - fileSizeInfoRef: any = React.createRef(); - fileNameInfoRef: any = React.createRef(); - - // TODO: Delete this.props.removeButtonColor - REMOVE_ICON_COLOR = - this.props.removeButtonColor || - this.props.style?.dropArea?.dropFile?.removeButton?.color || - this.props.style?.dropFile?.removeButton?.color || - this.props.style?.removeButton?.color || - REMOVE_ICON_DEFAULT_COLOR; - REMOVE_ICON_COLOR_LIGHT = lightenDarkenColor(this.REMOVE_ICON_COLOR, 40); - - state = { - dropAreaCustom: {}, - progressBar: 0, - displayProgressBarStatus: 'none', - file: null, - timeout: null, - files: null, - removeIconColor: this.REMOVE_ICON_COLOR, - isCanceled: false, - } as State; - - componentDidUpdate = (prevProps: any) => { - if (this.props.isReset !== prevProps.isReset) { - this.removeFile(); - } - }; - - componentDidMount = () => { - const currentDropAreaRef = this.dropAreaRef.current; - if (currentDropAreaRef) { - const fourDragsEvent = ['dragenter', 'dragover', 'dragleave', 'drop']; - fourDragsEvent.forEach((item) => { - currentDropAreaRef.addEventListener(item, this.preventDefaults, false); - }); - - if (!this.props.noDrag) { - const highlightDragsEvent = ['dragenter', 'dragover']; - highlightDragsEvent.forEach((item) => { - currentDropAreaRef.addEventListener(item, this.highlight, false); - }); - currentDropAreaRef.addEventListener( - 'dragleave', - this.unhighlight, - false, - ); - currentDropAreaRef.addEventListener('drop', this.unhighlight, false); - currentDropAreaRef.addEventListener( - 'drop', - this.visibleProgressBar, - false, - ); - currentDropAreaRef.addEventListener('drop', this.handleDrop, false); - } - } - }; - - componentWillUnmount = () => { - const currentDropAreaRef = this.dropAreaRef.current; - - const fourDragsEvent = ['dragenter', 'dragover', 'dragleave', 'drop']; - fourDragsEvent.forEach((item) => { - currentDropAreaRef.removeEventListener(item, this.preventDefaults, false); - }); - - if (!this.props.noDrag) { - const highlightDragsEvent = ['dragenter', 'dragover']; - highlightDragsEvent.forEach((item) => { - currentDropAreaRef.removeEventListener(item, this.highlight, false); - }); - currentDropAreaRef.removeEventListener( - 'dragleave', - this.unhighlight, - false, - ); - currentDropAreaRef.removeEventListener('drop', this.unhighlight, false); - currentDropAreaRef.removeEventListener( - 'drop', - this.visibleProgressBar, - false, - ); - currentDropAreaRef.removeEventListener('drop', this.handleDrop, false); - } - }; - - preventDefaults = (e: any) => { - e.preventDefault(); - e.stopPropagation(); - }; - - highlight = () => { - const { style } = this.props; - this.setState({ - dropAreaCustom: Object.assign( - {}, - style?.dropAreaActive - ? style?.dropAreaActive.borderColor - ? style?.dropAreaActive - : Object.assign({}, style?.dropAreaActive, styles.highlight) - : style?.dropArea?.dropAreaActive - ? style?.dropArea?.dropAreaActive.borderColor - ? style?.dropArea?.dropAreaActive - : Object.assign( - {}, - style?.dropArea?.dropAreaActive, - styles.highlight, - ) - : styles.highlight, - ), - }); - this.setState({ progressBar: 0 }); - }; - - unhighlight = () => { - this.setState({ - dropAreaCustom: Object.assign( - {}, - this.props.style?.dropArea?.borderColor - ? {} - : styles.dropAreaDefaultBorderColor, - ), - }); - }; - - visibleProgressBar = () => { - if (!this.props.noProgressBar) { - this.setState({ displayProgressBarStatus: 'block' }); - } - }; - - handleDrop = (e: any) => { - let files = null; - let isCanceled = false; - - if (e.files === undefined) { - const dt = e.dataTransfer; - files = dt.files; - } else { - files = e.files; - } - - if (files.length === 0) { - files = this.state.files; - isCanceled = true; - } - - this.setState({ files, isCanceled }, () => { - this.handleFiles(); - }); - }; - - handleFiles = () => { - this.setState({ progressBar: 0 }); - let files = null; - files = [...this.state.files]; - files.forEach(this.uploadFile); - }; - - uploadFile = (file: any) => { - this.displayFileInfo(file); - this.setState({ file }); - - const { onDrop, onFileLoad, onError, config = {} } = this.props; - const reader = new window.FileReader(); - let options = {}; - - const size = file.size; - const data: any = []; - let percent = 0; - - if (onDrop || onFileLoad) { - const self = this; - options = Object.assign( - { - complete: - config?.complete || config?.step - ? config.complete - : () => { - if (!onDrop && onFileLoad) { - onFileLoad(data, file); - } else if (onDrop && !onFileLoad) { - onDrop(data, file); - } - }, - step: config?.step - ? config.step - : (row: any) => { - data.push(row); - if (config && config.preview) { - percent = Math.round((data.length / config.preview) * 100); - self.setState({ progressBar: percent }); - if (data.length === config.preview) { - if (!onDrop && onFileLoad) { - onFileLoad(data, file); - } else if (onDrop && !onFileLoad) { - onDrop(data, file); - } - } - } else { - const progress = row.meta.cursor; - const newPercent = Math.round((progress / size) * 100); - if (newPercent === percent) { - return; - } - percent = newPercent; - } - self.setState({ progressBar: percent }); - }, - }, - options, - ); - } - - if (onError) { - options = Object.assign({ error: onError }, options); - } - - if (config) { - options = Object.assign({}, config, options); - } - - reader.onload = (e: any) => { - PapaParse.parse(e.target.result, options); - }; - - if (!this.props.noProgressBar) { - reader.onloadend = () => { - clearTimeout(this.state.timeout); - this.setState({ - timeout: setTimeout(() => { - this.disableProgressBar(); - }, 2000), - }); - }; - } - - reader.readAsText(file, config.encoding || 'utf-8'); - }; - - displayFileInfo = (file: any) => { - if (!this.childrenIsFunction()) { - this.fileSizeInfoRef.current.innerHTML = formatFileSize(file.size); - this.fileNameInfoRef.current.innerHTML = file.name; - } - }; - - disableProgressBar = () => { - if (!this.props.noProgressBar) { - this.setState({ displayProgressBarStatus: 'none' }); - } - }; - - childrenIsFunction = () => { - return typeof this.props.children === 'function'; - }; - - fileChange = (e: any) => { - const { target } = e; - if (!this.props.noProgressBar) { - this.setState({ displayProgressBarStatus: 'block' }, () => { - this.handleDrop(target); - }); - } else { - this.handleDrop(target); - } - }; - - open = (e: any) => { - const { displayProgressBarStatus } = this.state; - if (e && displayProgressBarStatus === 'none') { - this.preventDefaults(e); - this.inputFileRef.current.value = null; - this.inputFileRef.current.click(); - } - }; - - renderChildren = () => { - const { children } = this.props; - const { file, progressBar } = this.state; - return this.childrenIsFunction() - ? children({ file, progressBar }) - : children; - }; - - handleRemoveFile = (e: any) => { - if (e) { - e.stopPropagation(); - this.removeFile(); - } - }; - - removeFile = () => { - this.setState({ files: null, file: null }); - - const { onRemoveFile } = this.props; - if (onRemoveFile) { - onRemoveFile(null); - } - - this.inputFileRef.current.value = null; - }; - - changeRemoveIconColor = (color: string) => { - if (color) { - this.setState({ removeIconColor: color }); - } - }; - - renderDropFileRemoveButton = () => { - const { addRemoveButton } = this.props; - const { removeIconColor, displayProgressBarStatus } = this.state; - - if (addRemoveButton && displayProgressBarStatus === 'none') { - return ( -
this.handleRemoveFile(e)} - onMouseOver={() => - this.changeRemoveIconColor(this.REMOVE_ICON_COLOR_LIGHT) - } - onMouseOut={() => this.changeRemoveIconColor(this.REMOVE_ICON_COLOR)} - > - -
- ); - } - - if (addRemoveButton) { - return ( -
- -
- ); - } - - return null; - }; - - render() { - const { - style, - noClick, - children, - noProgressBar, - progressBarColor, - accept, - } = this.props; - const { - dropAreaCustom, - files, - isCanceled, - progressBar, - displayProgressBarStatus, - } = this.state; - - return ( - <> - this.fileChange(e)} - /> - {!this.childrenIsFunction() ? ( -
{ - if (!noClick) { - this.open(e); - } - }} - > - {files && files.length > 0 ? ( -
- {this.renderDropFileRemoveButton()} -
- - -
- {files && files.length > 0 && !isCanceled && !noProgressBar && ( - - )} -
- ) : ( - children - )} -
- ) : ( -
- {this.renderChildren()} - {files && files.length > 0 && !isCanceled && !noProgressBar && ( - - )} -
- )} - - ); - } -} diff --git a/src/RemoveIcon.tsx b/src/RemoveIcon.tsx deleted file mode 100644 index 6a1184a..0000000 --- a/src/RemoveIcon.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -export default function RemoveIcon(props: { color: string }) { - return ( - - - - - ); -} diff --git a/src/react-papaparse.ts b/src/react-papaparse.ts index 03efea4..5628ee3 100644 --- a/src/react-papaparse.ts +++ b/src/react-papaparse.ts @@ -4,21 +4,10 @@ export const BAD_DELIMITERS = PapaParse.BAD_DELIMITERS; export const RECORD_SEP = PapaParse.RECORD_SEP; export const UNIT_SEP = PapaParse.UNIT_SEP; export const WORKERS_SUPPORTED = PapaParse.WORKERS_SUPPORTED; - export const LocalChunkSize = PapaParse.LocalChunkSize; export const DefaultDelimiter = PapaParse.DefaultDelimiter; -// export { default as CSVReader } from './CSVReader'; -export { - default as CSVDownloader, - LINK_TYPE, - BUTTON_TYPE, -} from './CSVDownloader'; -export { readString } from './readString'; -export { readRemoteFile } from './readRemoteFile'; -export { jsonToCSV } from './jsonToCSV'; - +export { formatFileSize, lightenDarkenColor } from './utils'; export { usePapaParse } from './usePapaParse'; export { useCSVDownloader } from './useCSVDownloader'; export { useCSVReader } from './useCSVReader'; -export { formatFileSize, lightenDarkenColor } from './utils'; diff --git a/src/useCSVReader.tsx b/src/useCSVReader.tsx index 42cf92f..4c90ff9 100644 --- a/src/useCSVReader.tsx +++ b/src/useCSVReader.tsx @@ -179,7 +179,6 @@ function useCSVReaderComponent() { // Fn for opening the file dialog programmatically const openFileDialog = useCallback(() => { if (inputRef.current && state.displayProgressBar) { - // if (inputRef.current) { dispatch({ type: 'openDialog' }); inputRef.current.value = null; inputRef.current.click(); @@ -206,7 +205,7 @@ function useCSVReaderComponent() { window.addEventListener('focus', onWindowFocus, false); return () => { window.removeEventListener('focus', onWindowFocus, false); - } + }; }, [inputRef, isFileDialogActive]); // Cb to open the file dialog when click occurs on the dropzone From 23ce361a3701ec9d657ec40350f6419c08818526 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Mon, 10 Jan 2022 00:43:10 +0700 Subject: [PATCH 115/140] Update docs --- README.md | 18 +- demo/CSVDownloader1.tsx | 46 -- demo/CSVDownloader2.tsx | 22 - demo/CSVReader1.tsx | 64 -- demo/CSVReader2.tsx | 157 ---- demo/CSVReader3.tsx | 158 ---- demo/CSVReader4.tsx | 158 ---- demo/JsonToCSV.tsx | 42 -- demo/ReadRemoteFile.tsx | 19 - demo/ReadString.tsx | 26 - docs/package.json | 7 +- docs/pages/_app.js | 11 +- docs/pages/demo.js | 25 +- docs/pages/docs.js | 7 +- docs/pages/index.js | 8 +- docs/src/components/screens/Demo.js | 360 ++++++++- .../screens/indexes/CSVDownloader.js | 66 +- .../components/screens/indexes/CSVParsing.js | 32 +- .../components/screens/indexes/Delimiter.js | 14 +- .../components/screens/indexes/LocalFile.js | 697 +++++++++++++----- .../components/screens/indexes/RemoteFile.js | 25 +- docs/src/components/screens/indexes/Stream.js | 4 +- .../src/components/screens/indexes/Unparse.js | 46 +- .../src/components/screens/indexes/Welcome.js | 4 +- docs/src/components/screens/indexes/Worker.js | 2 +- docs/static/css/home.css | 10 +- docs/static/js/prism.js | 7 - 27 files changed, 1082 insertions(+), 953 deletions(-) delete mode 100644 demo/CSVDownloader1.tsx delete mode 100644 demo/CSVDownloader2.tsx delete mode 100644 demo/CSVReader1.tsx delete mode 100644 demo/CSVReader2.tsx delete mode 100644 demo/CSVReader3.tsx delete mode 100644 demo/CSVReader4.tsx delete mode 100644 demo/JsonToCSV.tsx delete mode 100644 demo/ReadRemoteFile.tsx delete mode 100644 demo/ReadString.tsx delete mode 100644 docs/static/js/prism.js diff --git a/README.md b/README.md index 0d5f1a6..65b1659 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ export default function CSVReader() { ```javascript import React, { useState, CSSProperties } from 'react'; + import { useCSVReader, lightenDarkenColor, @@ -278,7 +279,7 @@ export default function CSVReader() { event.preventDefault(); setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); }} - onMouseOut={(event) => { + onMouseOut={(event: Event) => { event.preventDefault(); setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); }} @@ -304,6 +305,7 @@ export default function CSVReader() { ```javascript import React, { useState, CSSProperties } from 'react'; + import { useCSVReader, lightenDarkenColor, @@ -443,7 +445,7 @@ export default function CSVReader() { event.preventDefault(); setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); }} - onMouseOut={(event) => { + onMouseOut={(event: Event) => { event.preventDefault(); setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); }} @@ -469,6 +471,7 @@ export default function CSVReader() { ```javascript import React, { useState, CSSProperties } from 'react'; + import { useCSVReader, lightenDarkenColor, @@ -608,7 +611,7 @@ export default function CSVReader() { event.preventDefault(); setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); }} - onMouseOut={(event) => { + onMouseOut={(event: Event) => { event.preventDefault(); setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); }} @@ -838,9 +841,10 @@ export default function JsonToCSV() { "Column 4": 7 } ]`; - const results = jsonToCSV(jsonData) + + const results = jsonToCSV(jsonData); console.log('---------------------------'); - console.log('Results:', results) + console.log('Results:', results); console.log('---------------------------'); }; @@ -879,10 +883,10 @@ const { readRemoteFile } = usePapaParse(); readRemoteFile(url, { step: (row) => { - console.log('Row:', row.data) + console.log('Row:', row.data); }, complete: () => { - console.log('All done!') + console.log('All done!'); } }); ``` diff --git a/demo/CSVDownloader1.tsx b/demo/CSVDownloader1.tsx deleted file mode 100644 index ea0dee0..0000000 --- a/demo/CSVDownloader1.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; - -import { useCSVDownloader } from 'react-papaparse'; - -export default function CSVDownloader() { - const { CSVDownloader, Type } = useCSVDownloader(); - - return ( - - Download - - ); -} diff --git a/demo/CSVDownloader2.tsx b/demo/CSVDownloader2.tsx deleted file mode 100644 index c3feddd..0000000 --- a/demo/CSVDownloader2.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; - -import { useCSVDownloader } from 'react-papaparse'; - -export default function CSVDownloader() { - const { CSVDownloader, Type } = useCSVDownloader(); - - return ( - - Download - - ); -} diff --git a/demo/CSVReader1.tsx b/demo/CSVReader1.tsx deleted file mode 100644 index bdcb84b..0000000 --- a/demo/CSVReader1.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { CSSProperties } from 'react'; - -import { useCSVReader } from 'react-papaparse'; - -const styles = { - csvReader: { - display: 'flex', - flexDirection: 'row', - marginBottom: 10, - } as CSSProperties, - browseFile: { - width: '20%', - } as CSSProperties, - acceptedFile: { - border: '1px solid #ccc', - height: 45, - lineHeight: 2.5, - paddingLeft: 10, - width: '80%', - } as CSSProperties, - remove: { - borderRadius: 0, - padding: '0 20px', - } as CSSProperties, - progressBarBackgroundColor: { - backgroundColor: 'red', - } as CSSProperties, -}; - -export default function CSVReader() { - const { CSVReader } = useCSVReader(); - - return ( - { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - }} - > - {({ - getRootProps, - acceptedFile, - ProgressBar, - getRemoveFileProps, - }: any) => ( - <> -
- -
- {acceptedFile && acceptedFile.name} -
- -
- - - )} -
- ); -} diff --git a/demo/CSVReader2.tsx b/demo/CSVReader2.tsx deleted file mode 100644 index 05ad48c..0000000 --- a/demo/CSVReader2.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import React, { useState, CSSProperties } from 'react'; -import { - useCSVReader, - lightenDarkenColor, - formatFileSize, -} from 'react-papaparse'; - -const GREY = '#CCC'; -const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; -const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; -const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( - DEFAULT_REMOVE_HOVER_COLOR, - 40, -); -const GREY_DIM = '#686868'; - -const styles = { - zone: { - alignItems: 'center', - border: `2px dashed ${GREY}`, - borderRadius: 20, - display: 'flex', - flexDirection: 'column', - height: '100%', - justifyContent: 'center', - padding: 20, - } as CSSProperties, - file: { - background: 'linear-gradient(to bottom, #EEE, #DDD)', - borderRadius: 20, - display: 'flex', - height: 120, - width: 120, - position: 'relative', - zIndex: 10, - flexDirection: 'column', - justifyContent: 'center', - } as CSSProperties, - info: { - alignItems: 'center', - display: 'flex', - flexDirection: 'column', - paddingLeft: 10, - paddingRight: 10, - } as CSSProperties, - size: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - marginBottom: '0.5em', - justifyContent: 'center', - display: 'flex', - } as CSSProperties, - name: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - fontSize: 12, - marginBottom: '0.5em', - } as CSSProperties, - progressBar: { - bottom: 14, - position: 'absolute', - width: '100%', - paddingLeft: 10, - paddingRight: 10, - } as CSSProperties, - zoneHover: { - borderColor: GREY_DIM, - } as CSSProperties, - default: { - borderColor: GREY, - } as CSSProperties, - remove: { - height: 23, - position: 'absolute', - right: 6, - top: 6, - width: 23, - } as CSSProperties, -}; - -export default function CSVReader() { - const { CSVReader } = useCSVReader(); - const [zoneHover, setZoneHover] = useState(false); - const [removeHoverColor, setRemoveHoverColor] = useState( - DEFAULT_REMOVE_HOVER_COLOR, - ); - - return ( - { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - setZoneHover(false); - }} - onDragOver={(event: DragEvent) => { - event.preventDefault(); - setZoneHover(true); - }} - onDragLeave={(event: DragEvent) => { - event.preventDefault(); - setZoneHover(false); - }} - > - {({ - getRootProps, - acceptedFile, - ProgressBar, - getRemoveFileProps, - Remove, - }: any) => ( - <> -
- {acceptedFile ? ( - <> -
-
- - {formatFileSize(acceptedFile.size)} - - {acceptedFile.name} -
-
- -
-
{ - event.preventDefault(); - setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); - }} - onMouseOut={(event) => { - event.preventDefault(); - setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); - }} - > - -
-
- - ) : ( - 'Drop CSV file here or click to upload' - )} -
- - )} -
- ); -} diff --git a/demo/CSVReader3.tsx b/demo/CSVReader3.tsx deleted file mode 100644 index 72c75d4..0000000 --- a/demo/CSVReader3.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import React, { useState, CSSProperties } from 'react'; -import { - useCSVReader, - lightenDarkenColor, - formatFileSize, -} from 'react-papaparse'; - -const GREY = '#CCC'; -const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; -const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; -const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( - DEFAULT_REMOVE_HOVER_COLOR, - 40, -); -const GREY_DIM = '#686868'; - -const styles = { - zone: { - alignItems: 'center', - border: `2px dashed ${GREY}`, - borderRadius: 20, - display: 'flex', - flexDirection: 'column', - height: '100%', - justifyContent: 'center', - padding: 20, - } as CSSProperties, - file: { - background: 'linear-gradient(to bottom, #EEE, #DDD)', - borderRadius: 20, - display: 'flex', - height: 120, - width: 120, - position: 'relative', - zIndex: 10, - flexDirection: 'column', - justifyContent: 'center', - } as CSSProperties, - info: { - alignItems: 'center', - display: 'flex', - flexDirection: 'column', - paddingLeft: 10, - paddingRight: 10, - } as CSSProperties, - size: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - marginBottom: '0.5em', - justifyContent: 'center', - display: 'flex', - } as CSSProperties, - name: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - fontSize: 12, - marginBottom: '0.5em', - } as CSSProperties, - progressBar: { - bottom: 14, - position: 'absolute', - width: '100%', - paddingLeft: 10, - paddingRight: 10, - } as CSSProperties, - zoneHover: { - borderColor: GREY_DIM, - } as CSSProperties, - default: { - borderColor: GREY, - } as CSSProperties, - remove: { - height: 23, - position: 'absolute', - right: 6, - top: 6, - width: 23, - } as CSSProperties, -}; - -export default function CSVReader() { - const { CSVReader } = useCSVReader(); - const [zoneHover, setZoneHover] = useState(false); - const [removeHoverColor, setRemoveHoverColor] = useState( - DEFAULT_REMOVE_HOVER_COLOR, - ); - - return ( - { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - setZoneHover(false); - }} - onDragOver={(event: DragEvent) => { - event.preventDefault(); - setZoneHover(true); - }} - onDragLeave={(event: DragEvent) => { - event.preventDefault(); - setZoneHover(false); - }} - noClick - > - {({ - getRootProps, - acceptedFile, - ProgressBar, - getRemoveFileProps, - Remove, - }: any) => ( - <> -
- {acceptedFile ? ( - <> -
-
- - {formatFileSize(acceptedFile.size)} - - {acceptedFile.name} -
-
- -
-
{ - event.preventDefault(); - setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); - }} - onMouseOut={(event) => { - event.preventDefault(); - setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); - }} - > - -
-
- - ) : ( - 'Drop CSV file here to upload' - )} -
- - )} -
- ); -} diff --git a/demo/CSVReader4.tsx b/demo/CSVReader4.tsx deleted file mode 100644 index a7d77d7..0000000 --- a/demo/CSVReader4.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import React, { useState, CSSProperties } from 'react'; -import { - useCSVReader, - lightenDarkenColor, - formatFileSize, -} from 'react-papaparse'; - -const GREY = '#CCC'; -const GREY_LIGHT = 'rgba(255, 255, 255, 0.4)'; -const DEFAULT_REMOVE_HOVER_COLOR = '#A01919'; -const REMOVE_HOVER_COLOR_LIGHT = lightenDarkenColor( - DEFAULT_REMOVE_HOVER_COLOR, - 40, -); -const GREY_DIM = '#686868'; - -const styles = { - zone: { - alignItems: 'center', - border: `2px dashed ${GREY}`, - borderRadius: 20, - display: 'flex', - flexDirection: 'column', - height: '100%', - justifyContent: 'center', - padding: 20, - } as CSSProperties, - file: { - background: 'linear-gradient(to bottom, #EEE, #DDD)', - borderRadius: 20, - display: 'flex', - height: 120, - width: 120, - position: 'relative', - zIndex: 10, - flexDirection: 'column', - justifyContent: 'center', - } as CSSProperties, - info: { - alignItems: 'center', - display: 'flex', - flexDirection: 'column', - paddingLeft: 10, - paddingRight: 10, - } as CSSProperties, - size: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - marginBottom: '0.5em', - justifyContent: 'center', - display: 'flex', - } as CSSProperties, - name: { - backgroundColor: GREY_LIGHT, - borderRadius: 3, - fontSize: 12, - marginBottom: '0.5em', - } as CSSProperties, - progressBar: { - bottom: 14, - position: 'absolute', - width: '100%', - paddingLeft: 10, - paddingRight: 10, - } as CSSProperties, - zoneHover: { - borderColor: GREY_DIM, - } as CSSProperties, - default: { - borderColor: GREY, - } as CSSProperties, - remove: { - height: 23, - position: 'absolute', - right: 6, - top: 6, - width: 23, - } as CSSProperties, -}; - -export default function CSVReader() { - const { CSVReader } = useCSVReader(); - const [zoneHover, setZoneHover] = useState(false); - const [removeHoverColor, setRemoveHoverColor] = useState( - DEFAULT_REMOVE_HOVER_COLOR, - ); - - return ( - { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - setZoneHover(false); - }} - onDragOver={(event: DragEvent) => { - event.preventDefault(); - setZoneHover(true); - }} - onDragLeave={(event: DragEvent) => { - event.preventDefault(); - setZoneHover(false); - }} - noDrag - > - {({ - getRootProps, - acceptedFile, - ProgressBar, - getRemoveFileProps, - Remove, - }: any) => ( - <> -
- {acceptedFile ? ( - <> -
-
- - {formatFileSize(acceptedFile.size)} - - {acceptedFile.name} -
-
- -
-
{ - event.preventDefault(); - setRemoveHoverColor(REMOVE_HOVER_COLOR_LIGHT); - }} - onMouseOut={(event) => { - event.preventDefault(); - setRemoveHoverColor(DEFAULT_REMOVE_HOVER_COLOR); - }} - > - -
-
- - ) : ( - 'Click to upload' - )} -
- - )} -
- ); -} diff --git a/demo/JsonToCSV.tsx b/demo/JsonToCSV.tsx deleted file mode 100644 index 4142af9..0000000 --- a/demo/JsonToCSV.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; - -import { usePapaParse } from 'react-papaparse'; - -export default function JsonToCSV() { - const { jsonToCSV } = usePapaParse(); - - const handleJsonToCSV = () => { - const jsonData = `[ - { - "Column 1": "1-1", - "Column 2": "1-2", - "Column 3": "1-3", - "Column 4": "1-4" - }, - { - "Column 1": "2-1", - "Column 2": "2-2", - "Column 3": "2-3", - "Column 4": "2-4" - }, - { - "Column 1": "3-1", - "Column 2": "3-2", - "Column 3": "3-3", - "Column 4": "3-4" - }, - { - "Column 1": 4, - "Column 2": 5, - "Column 3": 6, - "Column 4": 7 - } - ]`; - const results = jsonToCSV(jsonData); - console.log('---------------------------'); - console.log('Results:', results); - console.log('---------------------------'); - }; - - return ; -} diff --git a/demo/ReadRemoteFile.tsx b/demo/ReadRemoteFile.tsx deleted file mode 100644 index 0f24129..0000000 --- a/demo/ReadRemoteFile.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; - -import { usePapaParse } from 'react-papaparse'; - -export default function ReadRemoteFile() { - const { readRemoteFile } = usePapaParse(); - - const handleReadRemoteFile = () => { - readRemoteFile(url, { - complete: (results) => { - console.log('---------------------------'); - console.log('Results:', results); - console.log('---------------------------'); - }, - }); - }; - - return ; -} diff --git a/demo/ReadString.tsx b/demo/ReadString.tsx deleted file mode 100644 index d631062..0000000 --- a/demo/ReadString.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -import { usePapaParse } from 'react-papaparse'; - -export default function ReadString() { - const { readString } = usePapaParse(); - - const handleReadString = () => { - const csvString = `Column 1,Column 2,Column 3,Column 4 -1-1,1-2,1-3,1-4 -2-1,2-2,2-3,2-4 -3-1,3-2,3-3,3-4 -4,5,6,7`; - - readString(csvString, { - worker: true, - complete: (results) => { - console.log('---------------------------'); - console.log(results); - console.log('---------------------------'); - }, - }); - }; - - return ; -} diff --git a/docs/package.json b/docs/package.json index e275ef0..bd5cc99 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,10 +15,11 @@ "license": "MIT", "dependencies": { "next": "^10.1.3", + "prismjs": "^1.26.0", "raw-loader": "^4.0.0", - "react": "^16.12.0", - "react-dom": "^16.12.0", - "react-papaparse": "^3.18.0", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-papaparse": "file:..", "react-tabs": "^3.1.0" }, "devDependencies": { diff --git a/docs/pages/_app.js b/docs/pages/_app.js index c521700..13f9265 100644 --- a/docs/pages/_app.js +++ b/docs/pages/_app.js @@ -3,6 +3,7 @@ import Head from 'next/head'; import App from 'next/app'; import 'react-tabs/style/react-tabs.css'; +import 'prismjs/themes/prism-tomorrow.css'; class CustomApp extends App { // Only uncomment this method if you have blocking data requirements for @@ -21,6 +22,7 @@ class CustomApp extends App { const { Component, pageProps } = this.props; const pageName = this.props.router.route.substr(1); let title = ''; + if (pageName === '') { title = 'react-papaparse'; } else if (pageName === 'demo') { @@ -40,7 +42,7 @@ class CustomApp extends App { - {/* ====== SEO ======= */} + {/* == SEO == */} - {/* ================== */} + {/* ========= */} - + {/* */} {pageName === '' ? ( @@ -147,7 +149,7 @@ class CustomApp extends App { react-papaparse by{' '} Bunlong
- © 2018-2021 + © 2018-2022
@@ -165,7 +167,6 @@ class CustomApp extends App {
-