From 03fa8dab35204144f8e85d091b405e19caabed21 Mon Sep 17 00:00:00 2001 From: Bunlong Date: Sat, 28 Aug 2021 22:15:21 +0700 Subject: [PATCH 001/101] 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/101] 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/101] 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/101] 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 a302eee5baafa3a3333d7a4bc88e5d8ba75a7fcc Mon Sep 17 00:00:00 2001 From: Bunlong Date: Thu, 18 Nov 2021 23:44:21 +0700 Subject: [PATCH 005/101] 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 006/101] 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 007/101] 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 008/101] 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 009/101] 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 010/101] 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 011/101] 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 012/101] 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 013/101] 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 014/101] 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 015/101] 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 016/101] 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 017/101] 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 022/101] 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 023/101] 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 024/101] 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 025/101] 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 026/101] 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 027/101] 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 028/101] 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 029/101] 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 030/101] 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 031/101] 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 032/101] 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 033/101] 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 034/101] 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 035/101] 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 036/101] 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 037/101] 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 038/101] 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 039/101] 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 040/101] 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 041/101] 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 042/101] 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 043/101] 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 044/101] 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 045/101] 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 046/101] 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 047/101] 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 048/101] 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 049/101] 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 050/101] 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 051/101] 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 052/101] 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 053/101] 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 054/101] 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 055/101] 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 056/101] 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 057/101] 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 058/101] 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 059/101] 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 060/101] 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 061/101] 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 062/101] 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 063/101] 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 064/101] 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 065/101] 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 066/101] 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 3c674520a94d5364aa3cd60bcc4bec70b16d271c Mon Sep 17 00:00:00 2001 From: Bunlong Date: Wed, 5 Jan 2022 00:31:20 +0700 Subject: [PATCH 067/101] 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 068/101] 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 069/101] 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 070/101] 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 071/101] 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 072/101] 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 073/101] 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 074/101] 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 075/101] 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 076/101] 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 077/101] 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 078/101] 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 079/101] 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 080/101] 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 081/101] 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 082/101] 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 083/101] 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 084/101] 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 085/101] 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 086/101] 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 {
-