├── .github └── workflows │ ├── lint.yml │ └── publish.yml ├── LICENSE.md ├── README.md ├── assets └── images │ ├── apk-icon.png │ ├── banner.png │ ├── canvas.svg │ ├── cta.png │ ├── example-img.jpg │ ├── file-icon.png │ ├── loader.png │ ├── music-icon.png │ ├── pdf-icon.png │ ├── placeholder.png │ ├── sql-icon.png │ ├── text-icon.png │ └── zip-icon.png ├── gifs ├── canvas-view.gif ├── full.gif ├── long-square-view.gif ├── multiple-file-uploading.gif ├── round-view.gif ├── single-file-uploading.gif └── square-view.gif ├── react ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── README.md ├── examples │ ├── CanvasView.tsx │ ├── LongSquareView.tsx │ ├── RoundView.tsx │ ├── SquareView.tsx │ └── demo │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── README.md │ │ ├── next.config.js │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ │ ├── src │ │ ├── app │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ └── assets │ │ │ └── images │ │ │ ├── example-img.jpg │ │ │ ├── loader.png │ │ │ └── placeholder.png │ │ ├── tailwind.config.ts │ │ └── tsconfig.json ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ ├── next.svg │ └── vercel.svg ├── src │ └── app │ │ ├── components │ │ ├── multipleFileUpload.tsx │ │ └── singleFileUpload.tsx │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── index.ts │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── style.scss ├── tailwind.config.ts ├── tsconfig.json └── vite.config.js └── vue ├── .babelrc ├── .eslintrc.cjs ├── .gitignore ├── .npmignore ├── .prettierrc.json ├── README.md ├── env.d.ts ├── examples ├── CanvasView.vue ├── LongSquareView.vue ├── RoundView.vue ├── SquareView.vue └── demo │ ├── README.md │ ├── env.d.ts │ ├── index.html │ ├── package.json │ ├── public │ └── favicon.ico │ ├── src │ ├── App.vue │ ├── assets │ │ ├── logo.svg │ │ └── main.css │ ├── main.ts │ ├── router │ │ └── index.ts │ └── views │ │ └── IndexView.vue │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── index.html ├── package-lock.json ├── package.json ├── public └── favicon.ico ├── src ├── App.vue ├── assets │ └── logo.svg ├── components │ ├── MultipleFile.vue │ └── SingleFile.vue ├── index.ts ├── input.css ├── router │ └── index.ts ├── style.scss └── views │ └── IndexView.vue ├── tailwind.config.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Run lint in vue and react 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | vue-lint: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: "20.x" 14 | - run: | 15 | cd vue 16 | npm ci 17 | npm run lint 18 | 19 | react-lint: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions/setup-node@v3 24 | with: 25 | node-version: "20.x" 26 | - run: | 27 | cd react 28 | npm ci 29 | npm run lint 30 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | vue-publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: "20.x" 16 | registry-url: "https://registry.npmjs.org" 17 | - run: | 18 | cd vue 19 | npm ci 20 | npm run build 21 | 22 | # Check if the vue-file-upload version already exists on npm 23 | - name: Check Version Existence 24 | id: check_vue_version 25 | run: | 26 | if npm show @canopassoftware/vue-file-upload@1.0.8; then 27 | echo "Version already published. Skipping npm publish." 28 | echo "::set-output name=skip_vue_publish::true" 29 | else 30 | echo "::set-output name=skip_vue_publish::false" 31 | fi 32 | 33 | # Publish only if the vue-file-upload version check passed 34 | - name: Publish to npm 35 | if: steps.check_vue_version.outputs.skip_vue_publish != 'true' 36 | run: | 37 | cd vue 38 | npm publish --access public 39 | env: 40 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 41 | 42 | react-publish: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v4 46 | - uses: actions/setup-node@v3 47 | with: 48 | node-version: "20.x" 49 | registry-url: "https://registry.npmjs.org" 50 | - run: | 51 | cd react 52 | npm ci 53 | npm run build 54 | 55 | # Check if the react-file-upload version already exists on npm 56 | - name: Check Version Existence 57 | id: check_react_version 58 | run: | 59 | if npm show @canopassoftware/react-file-upload@1.2.0; then 60 | echo "Version already published. Skipping npm publish." 61 | echo "::set-output name=skip_react_publish::true" 62 | else 63 | echo "::set-output name=skip_react_publish::false" 64 | fi 65 | 66 | # Publish only if the react-file-upload version check passed 67 | - name: Publish to npm 68 | if: steps.check_react_version.outputs.skip_react_publish != 'true' 69 | run: | 70 | cd react 71 | npm publish --access public 72 | env: 73 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 74 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Canopas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # Web File Management with Preview - Fully Customized 4 | 5 | A versatile and user-friendly file management system built for Vue and React that allows single and multiple file uploading with a preview feature, returning an array of selected files. It supports custom designs by overriding the style classes. 6 | 7 | 8 | 9 | --- 10 | 11 | ### The library provides support for Vue and React. Each has its own set of features and information. Explore the individual folders for more details. 12 | 13 | ## Vue File Upload 14 | 15 | This folder includes files and resources related to the Vue file upload library. and also, it has [demo](./vue/examples/demo/) project. so, it's easy to understand and you can use it directly. 16 | 17 | Checkout the live demo on, codesandbox

18 | [![codesandbox.io](https://codesandbox.io/favicon.ico)](https://codesandbox.io/p/sandbox/cranky-breeze-r4hht7?file=%2Fsrc%2Fmain.js) 19 | 20 | For more details, check out the [Vue README](./vue/README.md). 21 | 22 | ## React File Upload 23 | 24 | This folder includes files and resources related to the React file upload library. and also, it has [demo](./react/examples/demo/) project. so, it's easy to understand and you can use it directly. 25 | 26 | Checkout the live demo on, codesandbox

27 | [![codesandbox.io](https://codesandbox.io/favicon.ico)](https://codesandbox.io/p/devbox/eager-mountain-n4zgs6?file=%2Fsrc%2FApp.tsx) 28 | 29 | For more details, check out the [React README](./react/README.md). 30 | 31 | Feel free to explore each folder for specific information about Vue and React! 32 | 33 | --- 34 | 35 | ## Table of Contents 36 | 37 | - [Features](#features) 38 | - [Installation](#installation) 39 | - [Vue](#vue) 40 | - [React](#react) 41 | - [Examples](#examples) 42 | - [Canvas View](#canvas-view) 43 | - [Square View](#square-view) 44 | - [Horizontal Long Square View](#horizontal-long-square-view) 45 | - [Circular View](#circular-view) 46 | - [Over-ride CSS](#over-ride-css) 47 | - [Contributing](#contributing) 48 | - [Credits](#credits) 49 | - [License](#license) 50 | 51 | --- 52 | 53 | ## Features 54 | 55 | - **Single File Upload:** Users can upload or change a single file. 56 | - **Multiple File Management:** Easily manage multiple files. 57 | - **File Preview:** Provides a preview of uploaded files (e.g., images, videos, gifs). 58 | - **Responsive Design:** Ensures a seamless experience on various devices. 59 | - **Fully customized:** Customize file upload UI as per your requirements 60 | 61 | --- 62 | 63 | ## Installation 64 | 65 | ### Vue 66 | 67 | Using npm: 68 | 69 | ``` 70 | npm install @canopassoftware/vue-file-upload 71 | ``` 72 | 73 | Using yarn: 74 | 75 | ``` 76 | yarn add @canopassoftware/vue-file-upload 77 | ``` 78 | 79 | --- 80 | 81 | ### React 82 | 83 | Using npm: 84 | 85 | ``` 86 | npm install @canopassoftware/react-file-upload 87 | ``` 88 | 89 | Using yarn: 90 | 91 | ``` 92 | yarn add @canopassoftware/react-file-upload 93 | ``` 94 | 95 | --- 96 | 97 | ## Examples 98 | 99 | We are providing some examples with design. so, you can easily take it and use into your project. 100 | 101 | ### Canvas View 102 | 103 | vue - [view code](./vue/examples/CanvasView.vue)
104 | react - [view code](./react/examples/CanvasView.tsx) 105 | 106 | 107 | 108 | ### Square View 109 | 110 | vue- [view code](./vue/examples/SquareView.vue)
111 | react - [view code](./react/examples/SquareView.tsx) 112 | 113 | 114 | 115 | ### Horizontal Long Square View 116 | 117 | vue - [view code](./vue/examples/LongSquareView.vue)
118 | react - [view code](./react/examples/LongSquareView.tsx) 119 | 120 | 121 | 122 | ### Circular View 123 | 124 | vue - [view code](./vue/examples/RoundView.vue)
125 | react - [view code](./react/examples/RoundView.tsx) 126 | 127 | 128 | 129 | ### Over-ride CSS 130 | 131 | For over-riding the design of default buttons, you can over-ride it's CSS by class name.
132 | For example.,
133 | 134 | - Over-ride CSS of remove file button you can add it like, 135 | 136 | ```css 137 | .remove-btn { 138 | color: white; 139 | background-color: red; 140 | font-size: 25px; 141 | padding: 5px; 142 | } 143 | ``` 144 | 145 | - Over-ride CSS of submit/upload file button you can add it like, 146 | 147 | ```css 148 | .upload-btn { 149 | color: white; 150 | background-color: rgb(51, 65, 85); 151 | font-size: 25px; 152 | padding: 5px 10px; 153 | } 154 | ``` 155 | 156 | --- 157 | 158 | ## Contributing 159 | 160 | We welcome contributions from the community. To contribute to this project, please follow these guidelines: 161 | 162 | - Fork the repository. 163 | - Create a new branch for your feature or bug fix. 164 | - Make your changes and commit them. 165 | - Push your changes to your fork. 166 | - Submit a pull request with a clear description of your changes. 167 | - Please ensure your code follows the project's coding standards and includes appropriate documentation. 168 | 169 | --- 170 | 171 | ## Credits 172 | 173 | This repository is owned and maintained by the [Canopas team](https://canopas.com/). If you are interested in building web apps or designing products, please let us know. We'd love to hear from you! 174 | 175 | 176 | 177 | --- 178 | 179 | ## License 180 | 181 | This project is licensed under the [MIT](https://github.com/canopas/web-file-upload/blob/main/LICENSE). 182 | -------------------------------------------------------------------------------- /assets/images/apk-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/apk-icon.png -------------------------------------------------------------------------------- /assets/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/banner.png -------------------------------------------------------------------------------- /assets/images/canvas.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/cta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/cta.png -------------------------------------------------------------------------------- /assets/images/example-img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/example-img.jpg -------------------------------------------------------------------------------- /assets/images/file-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/file-icon.png -------------------------------------------------------------------------------- /assets/images/loader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/loader.png -------------------------------------------------------------------------------- /assets/images/music-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/music-icon.png -------------------------------------------------------------------------------- /assets/images/pdf-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/pdf-icon.png -------------------------------------------------------------------------------- /assets/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/placeholder.png -------------------------------------------------------------------------------- /assets/images/sql-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/sql-icon.png -------------------------------------------------------------------------------- /assets/images/text-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/text-icon.png -------------------------------------------------------------------------------- /assets/images/zip-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/assets/images/zip-icon.png -------------------------------------------------------------------------------- /gifs/canvas-view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/gifs/canvas-view.gif -------------------------------------------------------------------------------- /gifs/full.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/gifs/full.gif -------------------------------------------------------------------------------- /gifs/long-square-view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/gifs/long-square-view.gif -------------------------------------------------------------------------------- /gifs/multiple-file-uploading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/gifs/multiple-file-uploading.gif -------------------------------------------------------------------------------- /gifs/round-view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/gifs/round-view.gif -------------------------------------------------------------------------------- /gifs/single-file-uploading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/gifs/single-file-uploading.gif -------------------------------------------------------------------------------- /gifs/square-view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/gifs/square-view.gif -------------------------------------------------------------------------------- /react/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | /dist 16 | 17 | # production 18 | /build 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # local env files 30 | .env*.local 31 | 32 | .vscode/* 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | 41 | # src/app/style.scss 42 | 43 | index.mjs 44 | index.umd.js 45 | next.svg 46 | vercel.svg 47 | style.css -------------------------------------------------------------------------------- /react/.npmignore: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------- /react/README.md: -------------------------------------------------------------------------------- 1 | # React File Management with Preview - Fully Customized 2 | 3 | React file management system built with React, Next.js and TypeScript that allows for single and multiple file uploading with a preview feature. It allows you to select files and preview them, returning an array of selected files. It allows customizing design by overriding the style classes. 4 | 5 | --- 6 | 7 | ## Table of Contents 8 | 9 | - [Getting Started](#getting-started) 10 | - [Prerequisites](#prerequisites) 11 | - [Installation](#installation) 12 | - [Properties and Events](#properties-and-events) 13 | - [Usage](#usage) 14 | 15 | --- 16 | 17 | ## Getting Started 18 | 19 | Follow below instructions to configure this package into your project. 20 | 21 | ### Prerequisites 22 | 23 | Before you begin, make sure you have the following software installed: 24 | 25 | - [Node.js](https://nodejs.org/) - v20.x 26 | - React 27 | 28 | ### Installation 29 | 30 | Using npm: 31 | 32 | ``` 33 | npm install @canopassoftware/react-file-upload 34 | ``` 35 | 36 | Using yarn: 37 | 38 | ``` 39 | yarn add @canopassoftware/react-file-upload 40 | ``` 41 | 42 | Note: we have already configure this in demo project. so, refer it for more information. 43 | 44 | --- 45 | 46 | ## Properties and Events 47 | 48 | ### props 49 | 50 | - **callback="handleFileUploading"** 51 | 52 | - `required` 53 | - **Description:** Add your upload callback function while receive the selected file/files 54 | 55 | - **uploadedFile="setPreviewFileData"** - For single file component 56 | 57 | - `required` 58 | - Uploaded file object with below format, 59 | ``` 60 | { 61 | fileType: string, 62 | fileUrl: string, 63 | fileName: string 64 | } 65 | ``` 66 | 67 | - **uploadedFiles="setPreviewFilesData"** - For multiple file component 68 | 69 | - `required` 70 | - Uploaded files array with below format, 71 | ``` 72 | [ 73 | { 74 | fileType: string, 75 | fileUrl: string, 76 | fileName: string 77 | } 78 | ] 79 | ``` 80 | 81 | - **uploadBtnText="'Upload'"** 82 | 83 | - **default** : Upload 84 | - Text for save or upload file button 85 | 86 | - **progressBtnText="'Uploading...'"** 87 | 88 | - **default** : Uploading... 89 | - Text for the progress bar, showing file uploading under the process 90 | 91 | - **removeBtnText="'Uploading...'"** 92 | 93 | - **default** : x 94 | - Text for remove file button 95 | 96 | - **accept="'image/jpg, image/jpeg, image/png, video/mp4, audio/mp3, application/zip'"** 97 | 98 | - Validation for file type. By default it will select all the type of file. 99 | 100 | - **(filetype)Preview="'(file location)'"** 101 | - **default** : Default file icons as per file types 102 | - Set path for your customized icon if needed 103 | 104 | --- 105 | 106 | ## Usage 107 | 108 | To manage and preview files with this library, follow these steps: 109 | 110 | ### Import the library into your file 111 | 112 | ```js 113 | // using CommonJS 114 | const { 115 | SingleFileUpload, 116 | MultipleFileUpload, 117 | } = require("@canopassoftware/react-file-upload"); 118 | 119 | OR; 120 | 121 | // using esModules 122 | import { 123 | SingleFileUpload, 124 | MultipleFileUpload, 125 | } from "@canopassoftware/react-file-upload"; 126 | ``` 127 | 128 | ### Use default CSS 129 | 130 | - Use `style.css` for UI by importing like, 131 | 132 | ```js 133 | import "@canopassoftware/react-file-upload/style.css"; 134 | ``` 135 | 136 | ### Creating custom UI with file preview 137 | 138 | - You can customize file uploading UI in inner part of component. 139 | - The `file` containing `file` object with following keys, we will use this object to show preview. 140 | 141 | ```js 142 | file = file: { 143 | previewType: 'video', // type of the preview. like, file is image or video 144 | previewUrl: 'data:image/jpeg;base64,/9j/D1AAAACRsdW1pAAAD...', // URL of the file preview 145 | previewName: 'a152148640581.62d918f12a0b4.mp4', // preview file name 146 | isDragging: false // you will get it `true` when you dragging the file on design 147 | } 148 | ``` 149 | 150 | ### Single File Upload Management 151 | 152 | ```js 153 | "use client"; 154 | 155 | import Image from "next/image"; 156 | import React, { useState } from "react"; 157 | import { SingleFileUpload } from '@canopassoftware/react-file-upload'; 158 | 159 | export default function App() { 160 | const [previewFileData, setPreviewFileData] = useState( 161 | {} as { 162 | previewType: string; 163 | previewUrl: string | ArrayBuffer | null; 164 | previewName: string; 165 | isDragging: boolean; 166 | } 167 | ); 168 | 169 | // callback function 170 | const handleFileUploading = async (file: any) => { 171 | await new Promise((resolve) => setTimeout(resolve, 2000)); 172 | setPreviewFileData({ 173 | previewType: "image", 174 | previewUrl: "https://picsum.photos/300/224", 175 | previewName: file.name, 176 | isDragging: false, 177 | }); 178 | }; 179 | ``` 180 | 181 | ```html 182 | return ( 183 |
184 | 190 | 191 | 192 |
193 | ); 194 | } 195 | ``` 196 | 197 | ### Multiple File Upload Management 198 | 199 | ```js 200 | "use client"; 201 | 202 | import Image, { StaticImageData } from "next/image"; 203 | import React from "react"; 204 | import MultipleFileUpload from "@canopassoftware/react-file-upload"; 205 | 206 | export default function App() { 207 | const uploadedFiles = [] as Array<{ 208 | fileType: string; 209 | fileUrl: string | StaticImageData; 210 | fileName: string; 211 | }>; 212 | 213 | // callback function 214 | const handleFilesUploading = async (files: any) => { 215 | const uploadedFiles = []; 216 | 217 | for (var i = 0; i < files.length; i++) { 218 | uploadedFiles.push({ 219 | fileType: "image", 220 | fileUrl: images[i], 221 | fileName: files[i].name, 222 | }); 223 | } 224 | 225 | await new Promise((resolve) => setTimeout(resolve, 5000)); 226 | return uploadedFiles; 227 | }; 228 | ``` 229 | 230 | ```html 231 | return ( 232 |
233 | 241 | {(file: any) => ( 242 | 243 | )} 244 | 245 |
246 | ); 247 | } 248 | ``` 249 | -------------------------------------------------------------------------------- /react/examples/CanvasView.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image, { StaticImageData } from "next/image"; 4 | import React, { useState } from "react"; 5 | import "@canopassoftware/react-file-upload/style.css"; 6 | import { 7 | SingleFileUpload, 8 | MultipleFileUpload, 9 | } from "@canopassoftware/react-file-upload"; 10 | 11 | export default function App() { 12 | // for single file upload component 13 | const [previewFileData, setPreviewFileData] = useState( 14 | {} as { 15 | previewType: string; 16 | previewUrl: string | StaticImageData | ArrayBuffer | null; 17 | previewName: string; 18 | isDragging: boolean; 19 | } 20 | ); 21 | 22 | const handleFileUploading = async (file: any) => { 23 | await new Promise((resolve) => setTimeout(resolve, 2000)); 24 | setPreviewFileData({ 25 | previewType: "image", 26 | previewUrl: "https://picsum.photos/300/224", 27 | previewName: file.name, 28 | isDragging: false, 29 | }); 30 | }; 31 | 32 | // for multiple file upload component 33 | const uploadedFiles = [] as Array<{ 34 | fileType: string; 35 | fileUrl: string | StaticImageData; 36 | fileName: string; 37 | }>; 38 | 39 | const handleFilesUploading = async (files: any) => { 40 | const uploadedFiles = []; 41 | 42 | for (var i = 0; i < files.length; i++) { 43 | uploadedFiles.push({ 44 | fileType: "image", 45 | fileUrl: "https://picsum.photos/300/224", 46 | fileName: files[i].name, 47 | }); 48 | } 49 | 50 | await new Promise((resolve) => setTimeout(resolve, 5000)); 51 | return uploadedFiles; 52 | }; 53 | 54 | return ( 55 |
56 |

Single File Upload

57 | 63 |
64 |
65 | {!previewFileData || !previewFileData.previewUrl ? ( 66 | 92 | ) : ( 93 |
94 | {previewFileData.previewType != "video" ? ( 95 | image 102 | ) : ( 103 | 113 | )} 114 |
115 | )} 116 |
117 |

118 | {previewFileData ? previewFileData.previewName : ""} 119 |

120 |
121 |
122 |
123 |
124 |

Multiple File Upload

125 | 132 | {(file: any) => ( 133 |
134 |
135 | {!file.previewUrl ? ( 136 | 162 | ) : ( 163 |
164 | {file.previewType != "video" ? ( 165 | image 172 | ) : ( 173 | 183 | )} 184 |
185 | )} 186 |
187 |

188 | {file ? file.previewName : ""} 189 |

190 |
191 | )} 192 |
193 |
194 | ); 195 | } 196 | -------------------------------------------------------------------------------- /react/examples/LongSquareView.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image, { StaticImageData } from "next/image"; 4 | import React, { useState } from "react"; 5 | import placeHolderImg from "../../assets/images/placeholder.png"; 6 | import "@canopassoftware/react-file-upload/style.css"; 7 | import { 8 | SingleFileUpload, 9 | MultipleFileUpload, 10 | } from "@canopassoftware/react-file-upload"; 11 | 12 | export default function App() { 13 | // for single file upload component 14 | const [previewFileData, setPreviewFileData] = useState( 15 | {} as { 16 | previewType: string; 17 | previewUrl: string | StaticImageData | ArrayBuffer | null; 18 | previewName: string; 19 | isDragging: boolean; 20 | } 21 | ); 22 | 23 | const handleFileUploading = async (file: any) => { 24 | await new Promise((resolve) => setTimeout(resolve, 2000)); 25 | setPreviewFileData({ 26 | previewType: "image", 27 | previewUrl: "https://picsum.photos/300/224", 28 | previewName: file.name, 29 | isDragging: false, 30 | }); 31 | }; 32 | 33 | // for multiple file upload component 34 | const uploadedFiles = [] as Array<{ 35 | fileType: string; 36 | fileUrl: string | StaticImageData; 37 | fileName: string; 38 | }>; 39 | 40 | const handleFilesUploading = async (files: any) => { 41 | const uploadedFiles = []; 42 | 43 | for (var i = 0; i < files.length; i++) { 44 | uploadedFiles.push({ 45 | fileType: "image", 46 | fileUrl: "https://picsum.photos/300/224", 47 | fileName: files[i].name, 48 | }); 49 | } 50 | 51 | await new Promise((resolve) => setTimeout(resolve, 5000)); 52 | return uploadedFiles; 53 | }; 54 | 55 | return ( 56 |
57 |

Single File Upload

58 | 64 |
65 |
66 |
67 | {previewFileData.previewType != "video" ? ( 68 | image 79 | ) : ( 80 | 90 | )} 91 |
92 |

93 | {previewFileData.previewName 94 | ? previewFileData.previewName 95 | : "Click to upload or drag and drop files"} 96 |

97 |
98 |
99 |
100 |
101 |
102 |

Multiple File Upload

103 | 110 | {(file: any) => ( 111 |
112 |
113 |
114 | {file.previewType != "video" ? ( 115 | image 126 | ) : ( 127 | 134 | )} 135 |
136 |

137 | {file.previewName 138 | ? file.previewName 139 | : "Click to upload or drag and drop files"} 140 |

141 |
142 |
143 | )} 144 |
145 |
146 | ); 147 | } 148 | -------------------------------------------------------------------------------- /react/examples/RoundView.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image, { StaticImageData } from "next/image"; 4 | import React, { useState } from "react"; 5 | import placeHolderImg from "../../assets/images/placeholder.png"; 6 | import "@canopassoftware/react-file-upload/style.css"; 7 | import { 8 | SingleFileUpload, 9 | MultipleFileUpload, 10 | } from "@canopassoftware/react-file-upload"; 11 | 12 | export default function App() { 13 | // for single file upload component 14 | const [previewFileData, setPreviewFileData] = useState( 15 | {} as { 16 | previewType: string; 17 | previewUrl: string | StaticImageData | ArrayBuffer | null; 18 | previewName: string; 19 | isDragging: boolean; 20 | } 21 | ); 22 | 23 | const handleFileUploading = async (file: any) => { 24 | await new Promise((resolve) => setTimeout(resolve, 2000)); 25 | setPreviewFileData({ 26 | previewType: "image", 27 | previewUrl: "https://picsum.photos/300/224", 28 | previewName: file.name, 29 | isDragging: false, 30 | }); 31 | }; 32 | 33 | // for multiple file upload component 34 | const uploadedFiles = [] as Array<{ 35 | fileType: string; 36 | fileUrl: string | StaticImageData; 37 | fileName: string; 38 | }>; 39 | 40 | const handleFilesUploading = async (files: any) => { 41 | const uploadedFiles = []; 42 | 43 | for (var i = 0; i < files.length; i++) { 44 | uploadedFiles.push({ 45 | fileType: "image", 46 | fileUrl: "https://picsum.photos/300/224", 47 | fileName: files[i].name, 48 | }); 49 | } 50 | 51 | await new Promise((resolve) => setTimeout(resolve, 5000)); 52 | return uploadedFiles; 53 | }; 54 | 55 | return ( 56 |
57 |

Single File Upload

58 | 64 |
65 |
66 |
67 | {previewFileData.previewType != "video" ? ( 68 | image 79 | ) : ( 80 | 90 | )} 91 |
92 |
93 |

94 | {previewFileData.previewName} 95 |

96 |
97 |
98 |
99 |
100 |

Multiple File Upload

101 | 108 | {(file: any) => ( 109 |
110 |
111 |
112 | {file.previewType != "video" ? ( 113 | image 124 | ) : ( 125 | 132 | )} 133 |
134 |
135 |

136 | {file.previewName} 137 |

138 |
139 | )} 140 |
141 |
142 | ); 143 | } 144 | -------------------------------------------------------------------------------- /react/examples/SquareView.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image, { StaticImageData } from "next/image"; 4 | import React, { useState } from "react"; 5 | import placeHolderImg from "../../assets/images/placeholder.png"; 6 | import "@canopassoftware/react-file-upload/style.css"; 7 | import { 8 | SingleFileUpload, 9 | MultipleFileUpload, 10 | } from "@canopassoftware/react-file-upload"; 11 | 12 | export default function App() { 13 | // for single file upload component 14 | const [previewFileData, setPreviewFileData] = useState( 15 | {} as { 16 | previewType: string; 17 | previewUrl: string | StaticImageData | ArrayBuffer | null; 18 | previewName: string; 19 | isDragging: boolean; 20 | } 21 | ); 22 | 23 | const handleFileUploading = async (file: any) => { 24 | await new Promise((resolve) => setTimeout(resolve, 2000)); 25 | setPreviewFileData({ 26 | previewType: "image", 27 | previewUrl: "https://picsum.photos/300/224", 28 | previewName: file.name, 29 | isDragging: false, 30 | }); 31 | }; 32 | 33 | // for multiple file upload component 34 | const uploadedFiles = [] as Array<{ 35 | fileType: string; 36 | fileUrl: string | StaticImageData; 37 | fileName: string; 38 | }>; 39 | 40 | const handleFilesUploading = async (files: any) => { 41 | const uploadedFiles = []; 42 | 43 | for (var i = 0; i < files.length; i++) { 44 | uploadedFiles.push({ 45 | fileType: "image", 46 | fileUrl: "https://picsum.photos/300/224", 47 | fileName: files[i].name, 48 | }); 49 | } 50 | 51 | await new Promise((resolve) => setTimeout(resolve, 5000)); 52 | return uploadedFiles; 53 | }; 54 | 55 | return ( 56 |
57 |

Single File Upload

58 | 64 |
65 |
66 |
67 | {previewFileData.previewType != "video" ? ( 68 | image 79 | ) : ( 80 | 90 | )} 91 |
92 |
93 |

94 | {previewFileData.previewName} 95 |

96 |
97 |
98 |
99 |
100 |

Multiple File Upload

101 | 108 | {(file: any) => ( 109 |
110 |
111 |
112 | {file.previewType != "video" ? ( 113 | image 124 | ) : ( 125 | 132 | )} 133 |
134 |
135 |

136 | {file.previewName} 137 |

138 |
139 | )} 140 |
141 |
142 | ); 143 | } 144 | -------------------------------------------------------------------------------- /react/examples/demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /react/examples/demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /react/examples/demo/README.md: -------------------------------------------------------------------------------- 1 | # test-react-file-upload 2 | 3 | This ready-to-use example shows real-time usage of the `react-file-upload` library with custom UI. 4 | 5 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 6 | 7 | ## Getting Started 8 | 9 | First, run the development server: 10 | 11 | ```bash 12 | npm run dev 13 | # or 14 | yarn dev 15 | # or 16 | pnpm dev 17 | # or 18 | bun dev 19 | ``` 20 | 21 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 22 | 23 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 24 | 25 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 26 | 27 | ## Learn More 28 | 29 | To learn more about Next.js, take a look at the following resources: 30 | 31 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 32 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 33 | 34 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 35 | 36 | ## Deploy on Vercel 37 | 38 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 39 | 40 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 41 | -------------------------------------------------------------------------------- /react/examples/demo/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | domains: ["picsum.photos"], 5 | }, 6 | }; 7 | 8 | module.exports = nextConfig; 9 | -------------------------------------------------------------------------------- /react/examples/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-react-file-upload", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@canopassoftware/react-file-upload": "^1.2.0", 13 | "next": "14.0.4", 14 | "react": "^18", 15 | "react-dom": "^18" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^20", 19 | "@types/react": "^18", 20 | "@types/react-dom": "^18", 21 | "autoprefixer": "^10.0.1", 22 | "eslint": "^8", 23 | "eslint-config-next": "14.0.4", 24 | "postcss": "^8", 25 | "tailwindcss": "^3.3.0", 26 | "typescript": "^5" 27 | } 28 | } -------------------------------------------------------------------------------- /react/examples/demo/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /react/examples/demo/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react/examples/demo/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react/examples/demo/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/react/examples/demo/src/app/favicon.ico -------------------------------------------------------------------------------- /react/examples/demo/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /react/examples/demo/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "Create Next App", 9 | description: "Generated by create next app", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: { 15 | children: React.ReactNode; 16 | }) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /react/examples/demo/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image, { StaticImageData } from "next/image"; 4 | import React, { useState } from "react"; 5 | import "@canopassoftware/react-file-upload/style.css"; 6 | import { 7 | SingleFileUpload, 8 | MultipleFileUpload, 9 | } from "@canopassoftware/react-file-upload"; 10 | import img from "../assets/images/example-img.jpg"; 11 | import placeHolderImg from "../assets/images/placeholder.png"; 12 | 13 | export default function App() { 14 | const [previewFileData, setPreviewFileData] = useState( 15 | {} as { 16 | previewType: string; 17 | previewUrl: string | StaticImageData | ArrayBuffer | null; 18 | previewName: string; 19 | isDragging: boolean; 20 | } 21 | ); 22 | 23 | const handleFileUploading = async (file: any) => { 24 | await new Promise((resolve) => setTimeout(resolve, 2000)); 25 | setPreviewFileData({ 26 | previewType: "image", 27 | previewUrl: img, 28 | previewName: file.name, 29 | isDragging: false, 30 | }); 31 | }; 32 | 33 | const uploadedFiles = [] as Array<{ 34 | fileType: string; 35 | fileUrl: string | StaticImageData; 36 | fileName: string; 37 | }>; 38 | 39 | const handleFilesUploading = async (files: any) => { 40 | const uploadedFiles = []; 41 | 42 | for (var i = 0; i < files.length; i++) { 43 | uploadedFiles.push({ 44 | fileType: "image", 45 | fileUrl: img, 46 | fileName: files[i].name, 47 | }); 48 | } 49 | 50 | await new Promise((resolve) => setTimeout(resolve, 5000)); 51 | return uploadedFiles; 52 | }; 53 | 54 | return ( 55 |
56 |

Single File Upload

57 | 63 |
64 |
65 | {!previewFileData || !previewFileData.previewUrl ? ( 66 | 92 | ) : ( 93 |
94 | {previewFileData.previewType != "video" ? ( 95 | image 102 | ) : ( 103 | 113 | )} 114 |
115 | )} 116 |
117 |

118 | {previewFileData ? previewFileData.previewName : ""} 119 |

120 |
121 |
122 |
123 |
124 |

Multiple File Upload

125 | 132 | {(file: any) => ( 133 |
134 |
135 | {!file.previewUrl ? ( 136 | 162 | ) : ( 163 |
164 | {file.previewType != "video" ? ( 165 | image 172 | ) : ( 173 | 183 | )} 184 |
185 | )} 186 |
187 |

{file ? file.previewName : ""}

188 |
189 | )} 190 |
191 |

Single File Upload

192 | 198 |
199 |
200 |
201 | {previewFileData.previewType != "video" ? ( 202 | image 213 | ) : ( 214 | 224 | )} 225 |
226 |

227 | {previewFileData.previewName 228 | ? previewFileData.previewName 229 | : "Click to upload or drag and drop files"} 230 |

231 |
232 |
233 |
234 |
235 |
236 |

Multiple File Upload

237 | 244 | {(file: any) => ( 245 |
246 |
247 |
248 | {file.previewType != "video" ? ( 249 | image 260 | ) : ( 261 | 268 | )} 269 |
270 |

271 | {file.previewName 272 | ? file.previewName 273 | : "Click to upload or drag and drop files"} 274 |

275 |
276 |
277 | )} 278 |
279 |

Single File Upload

280 | 286 |
287 |
288 |
289 | {previewFileData.previewType != "video" ? ( 290 | image 301 | ) : ( 302 | 312 | )} 313 |
314 |
315 |

316 | {previewFileData.previewName} 317 |

318 |
319 |
320 |
321 |
322 |

Multiple File Upload

323 | 330 | {(file: any) => ( 331 |
332 |
333 |
334 | {file.previewType != "video" ? ( 335 | image 346 | ) : ( 347 | 354 | )} 355 |
356 |
357 |

358 | {file.previewName} 359 |

360 |
361 | )} 362 |
363 |

Single File Upload

364 | 370 |
371 |
372 |
373 | {previewFileData.previewType != "video" ? ( 374 | image 385 | ) : ( 386 | 396 | )} 397 |
398 |
399 |

400 | {previewFileData.previewName} 401 |

402 |
403 |
404 |
405 |
406 |

Multiple File Upload

407 | 414 | {(file: any) => ( 415 |
416 |
417 |
418 | {file.previewType != "video" ? ( 419 | image 430 | ) : ( 431 | 438 | )} 439 |
440 |
441 |

442 | {file.previewName} 443 |

444 |
445 | )} 446 |
447 |
448 | ); 449 | } 450 | -------------------------------------------------------------------------------- /react/examples/demo/src/assets/images/example-img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/react/examples/demo/src/assets/images/example-img.jpg -------------------------------------------------------------------------------- /react/examples/demo/src/assets/images/loader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/react/examples/demo/src/assets/images/loader.png -------------------------------------------------------------------------------- /react/examples/demo/src/assets/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/react/examples/demo/src/assets/images/placeholder.png -------------------------------------------------------------------------------- /react/examples/demo/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /react/examples/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /react/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@canopassoftware/react-file-upload", 3 | "version": "1.2.0", 4 | "description": "Show the preview of file and manage files data to upload", 5 | "main": "index.umd.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/canopas/react-file-upload.git" 9 | }, 10 | "keywords": [ 11 | "file-upload", 12 | "image-upload", 13 | "file-management", 14 | "file-preview", 15 | "image-preview" 16 | ], 17 | "author": "dharti", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/canopas/react-file-upload/issues" 21 | }, 22 | "homepage": "https://github.com/canopas/react-file-upload#readme", 23 | "scripts": { 24 | "dev": "next dev", 25 | "build": "npm run build-css && vite build && cp -r ./dist/* ./", 26 | "start": "next start", 27 | "build-only": "next build", 28 | "lint": "next lint", 29 | "build-css": "tailwindcss -i ./src/app/globals.css -o ./dist/css/output.css --minify && cp ./src/app/style.scss ./dist/css/style.scss" 30 | }, 31 | "dependencies": { 32 | "next": "^14.0.4", 33 | "react": "^18", 34 | "react-dom": "^18", 35 | "sass": "^1.69.5" 36 | }, 37 | "devDependencies": { 38 | "@babel/cli": "^7.23.0", 39 | "@babel/core": "^7.23.3", 40 | "@babel/preset-env": "^7.23.3", 41 | "@types/node": "^20", 42 | "@types/react": "^18.2.37", 43 | "@types/react-dom": "^18.2.15", 44 | "autoprefixer": "^10", 45 | "eslint": "^8", 46 | "eslint-config-next": "14.0.0", 47 | "postcss": "^8", 48 | "prettier": "^3.0.3", 49 | "prettier-plugin-tailwindcss": "^0.5.6", 50 | "tailwindcss": "^3", 51 | "typescript": "^5.3.2", 52 | "vite": "^5.0.11" 53 | }, 54 | "directories": { 55 | "example": "examples" 56 | }, 57 | "files": [ 58 | "./index.mjs", 59 | "./index.umd.js", 60 | "./style.css" 61 | ] 62 | } -------------------------------------------------------------------------------- /react/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /react/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react/src/app/components/multipleFileUpload.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useState, useRef, RefObject } from "react"; 4 | import pdfPreviewImg from "../../../../assets/images/pdf-icon.png"; 5 | import textPreviewImg from "../../../../assets/images/text-icon.png"; 6 | import audioPreviewImg from "../../../../assets/images/music-icon.png"; 7 | import apkPreviewImg from "../../../../assets/images/apk-icon.png"; 8 | import zipPreviewImg from "../../../../assets/images/zip-icon.png"; 9 | import sqlPreviewImg from "../../../../assets/images/sql-icon.png"; 10 | import filePreviewImg from "../../../../assets/images/file-icon.png"; 11 | import { StaticImageData } from "next/image"; 12 | type InputElementType = HTMLInputElement; 13 | 14 | interface Props { 15 | accept?: string; 16 | uploadedFiles?: { 17 | fileType: string; 18 | fileUrl: string | StaticImageData; 19 | fileName: string; 20 | }[]; 21 | callback: Function; 22 | removeBtnText?: string; 23 | uploadBtnText?: string; 24 | progressBtnText?: string; 25 | pdfPreview?: string | StaticImageData; 26 | textPreview?: string | StaticImageData; 27 | audioPreview?: string | StaticImageData; 28 | apkPreview?: string | StaticImageData; 29 | zipPreview?: string | StaticImageData; 30 | sqlPreview?: string | StaticImageData; 31 | filePreview?: string | StaticImageData; 32 | children: (file: {}) => React.ReactNode; 33 | } 34 | 35 | export default function MultipleFileUpload({ 36 | accept = "", 37 | uploadedFiles = [] as Array<{ 38 | fileType: string; 39 | fileUrl: string | StaticImageData; 40 | fileName: string; 41 | }>, 42 | callback, 43 | removeBtnText = "x", 44 | uploadBtnText = "Upload", 45 | progressBtnText = "Uploading...", 46 | pdfPreview = pdfPreviewImg, 47 | textPreview = textPreviewImg, 48 | audioPreview = audioPreviewImg, 49 | apkPreview = apkPreviewImg, 50 | zipPreview = zipPreviewImg, 51 | sqlPreview = sqlPreviewImg, 52 | filePreview = filePreviewImg, 53 | children, 54 | }: Props) { 55 | const defaultPreview = [] as Array<{ 56 | previewType: string; 57 | previewUrl: string | StaticImageData | ArrayBuffer | null; 58 | previewName: string; 59 | isDragging: boolean; 60 | }>; 61 | 62 | const defaultFiles = [] as Array<{ 63 | fileType: string; 64 | fileUrl: string | StaticImageData; 65 | fileName: string; 66 | }>; 67 | 68 | if (uploadedFiles) { 69 | for (var i = 0; i < uploadedFiles.length; i++) { 70 | const obj = { 71 | previewType: uploadedFiles[i].fileType, 72 | previewUrl: uploadedFiles[i].fileUrl, 73 | previewName: uploadedFiles[i].fileName, 74 | isDragging: false, 75 | }; 76 | 77 | defaultPreview.push(obj); 78 | defaultFiles.push(uploadedFiles[i]); 79 | } 80 | } 81 | 82 | const [filesPreview, setFilesPreview] = useState(defaultPreview); 83 | const [isUploading, setIsUploading] = useState(false); 84 | const [files, setFiles] = useState(defaultFiles); 85 | 86 | const inputRefs = useRef | null>>( 87 | new Array().fill(null) 88 | ); 89 | const selectFile = (index: number) => { 90 | if (isUploading) { 91 | return; 92 | } 93 | if (inputRefs.current[index] && inputRefs.current[index]?.current) { 94 | inputRefs.current[index]?.current.click(); 95 | } 96 | }; 97 | 98 | const inputsRef = useRef(null); 99 | const selectFiles = () => { 100 | if (isUploading) { 101 | return; 102 | } 103 | if (inputsRef.current) { 104 | (inputsRef.current as HTMLInputElement).click(); 105 | } 106 | }; 107 | 108 | const add = ( 109 | previewType: any, 110 | previewUrl: string | StaticImageData | ArrayBuffer | null, 111 | previewName: string, 112 | isDragging: boolean, 113 | file: any 114 | ) => { 115 | // add file 116 | setFiles((files) => { 117 | return [...files, file]; 118 | }); 119 | // add file preview 120 | setFilesPreview((filesPreview) => { 121 | return [ 122 | ...filesPreview, 123 | { 124 | previewType: previewType, 125 | previewUrl: previewUrl, 126 | previewName: previewName, 127 | isDragging: isDragging, 128 | }, 129 | ]; 130 | }); 131 | }; 132 | 133 | const update = ( 134 | previewType: any, 135 | previewUrl: string | StaticImageData | ArrayBuffer | null, 136 | previewName: string, 137 | isDragging: boolean, 138 | file: any, 139 | index: number 140 | ) => { 141 | // update file 142 | setFiles((files) => { 143 | const newFiles = [...files]; 144 | newFiles[index] = file; 145 | return newFiles; 146 | }); 147 | // update file preview 148 | setFilesPreview((filesPreview) => { 149 | const newFiles = [...filesPreview]; 150 | newFiles[index].previewType = previewType; 151 | newFiles[index].previewUrl = previewUrl; 152 | newFiles[index].previewName = previewName; 153 | newFiles[index].isDragging = isDragging; 154 | return newFiles; 155 | }); 156 | }; 157 | 158 | const handleFileChange = (event: any, index: number, action: string) => { 159 | const files = event.target.files; 160 | for (var i = 0; i < files.length; i++) { 161 | try { 162 | previewFile(files[i], index + i, action); 163 | } catch (error) { 164 | console.error("error : ", error); 165 | } 166 | } 167 | }; 168 | 169 | const previewFile = (file: any, index: number, action: string) => { 170 | var obj = { 171 | previewType: "image", 172 | previewUrl: "" as string | StaticImageData | ArrayBuffer | null, 173 | previewName: file.name, 174 | isDragging: false, 175 | }; 176 | 177 | const reader = new FileReader(); 178 | reader.onload = () => { 179 | if (file.type.startsWith("image/")) { 180 | obj.previewUrl = reader.result; 181 | } else if (file.type === "text/plain") { 182 | obj.previewUrl = textPreview; 183 | } else if (file.type === "application/pdf") { 184 | obj.previewUrl = pdfPreview; 185 | } else if (file.type.startsWith("video/")) { 186 | obj.previewType = "video"; 187 | obj.previewUrl = URL.createObjectURL(file); 188 | } else if (file.type.startsWith("audio/")) { 189 | obj.previewUrl = audioPreview; 190 | } else if (file.type === "application/vnd.android.package-archive") { 191 | obj.previewUrl = apkPreview; 192 | } else if (file.type === "application/zip") { 193 | obj.previewUrl = zipPreview; 194 | } else if (file.type === "application/sql") { 195 | obj.previewUrl = sqlPreview; 196 | } else { 197 | obj.previewUrl = filePreview; 198 | } 199 | obj.previewName = file.name; 200 | 201 | if (action == "reset") { 202 | update( 203 | obj.previewType, 204 | obj.previewUrl, 205 | obj.previewName, 206 | obj.isDragging, 207 | file, 208 | index 209 | ); 210 | } else { 211 | add( 212 | obj.previewType, 213 | obj.previewUrl, 214 | obj.previewName, 215 | obj.isDragging, 216 | file 217 | ); 218 | } 219 | }; 220 | reader.onerror = (error) => { 221 | console.error(`Error while reading file ${file.name}: ${error}`); 222 | }; 223 | reader.readAsDataURL(file); 224 | }; 225 | 226 | const uploadingFunction = async () => { 227 | if (isUploading) { 228 | return; 229 | } 230 | setIsUploading(true); 231 | 232 | const gotFiles = await callback(files); 233 | 234 | setFilesPreview([]); 235 | setFiles([]); 236 | 237 | if (gotFiles) { 238 | for (var i = 0; i < gotFiles.length; i++) { 239 | const obj = { 240 | previewType: gotFiles[i].fileType, 241 | previewUrl: gotFiles[i].fileUrl, 242 | previewName: gotFiles[i].fileName, 243 | isDragging: false, 244 | }; 245 | 246 | setFilesPreview((filesPreview) => { 247 | return [...filesPreview, obj]; 248 | }); 249 | 250 | const file = gotFiles[i]; 251 | 252 | setFiles((files) => { 253 | return [...files, file]; 254 | }); 255 | } 256 | } 257 | 258 | setIsUploading(false); 259 | }; 260 | 261 | const handleDragOver = (event: any, index: number, action: string) => { 262 | if (isUploading) { 263 | return; 264 | } 265 | event.preventDefault(); 266 | if (action == "reset") { 267 | filesPreview[index].isDragging = true; 268 | } 269 | }; 270 | 271 | const handleDragLeave = (event: any, index: number, action: string) => { 272 | event.preventDefault(); 273 | if (action == "reset") { 274 | filesPreview[index].isDragging = false; 275 | } 276 | }; 277 | 278 | const handleDrop = (event: any, index: number, action: string) => { 279 | if (isUploading) { 280 | return; 281 | } 282 | event.preventDefault(); 283 | if (action == "reset") { 284 | filesPreview[index].isDragging = false; 285 | } 286 | const files = event.dataTransfer.files; 287 | for (var i = 0; i < files.length; i++) { 288 | try { 289 | if (accept ? accept.split(", ").includes(files[i].type) : true) { 290 | previewFile(files[i], index + i, action); 291 | } 292 | } catch (error) { 293 | console.error("error : ", error); 294 | } 295 | } 296 | }; 297 | 298 | const removeImg = (index: any) => { 299 | if (isUploading) { 300 | return; 301 | } 302 | // remove file 303 | setFiles((files) => { 304 | const newFiles = [...files]; 305 | newFiles.splice(index, 1); 306 | return newFiles; 307 | }); 308 | // remove file preview 309 | setFilesPreview((filesPreview) => { 310 | const newFiles = [...filesPreview]; 311 | newFiles.splice(index, 1); 312 | return newFiles; 313 | }); 314 | }; 315 | 316 | return ( 317 |
318 | {filesPreview.map((item: any, index: any) => ( 319 |
320 |
selectFile(index)} 323 | onDragOver={(event) => handleDragOver(event, index, "reset")} 324 | onDragLeave={(event) => handleDragLeave(event, index, "reset")} 325 | onDrop={(event) => handleDrop(event, index, "reset")} 326 | > 327 | {children && children(item)} 328 | { 331 | inputRefs.current[index] = element 332 | ? { current: element } 333 | : null; 334 | }} 335 | className="hidden" 336 | onChange={(event) => handleFileChange(event, index, "reset")} 337 | /> 338 |
339 | 345 |
346 | ))} 347 |
348 |
352 | handleDragOver(event, filesPreview.length, "add") 353 | } 354 | onDragLeave={(event) => 355 | handleDragLeave(event, filesPreview.length, "add") 356 | } 357 | onDrop={(event) => handleDrop(event, filesPreview.length, "add")} 358 | > 359 | {children({})} 360 | 365 | handleFileChange(event, filesPreview.length, "add") 366 | } 367 | multiple 368 | /> 369 |
370 |
377 | 389 |
390 |
391 |
392 | ); 393 | } 394 | -------------------------------------------------------------------------------- /react/src/app/components/singleFileUpload.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useState, useRef } from "react"; 4 | import pdfPreviewImg from "../../../../assets/images/pdf-icon.png"; 5 | import textPreviewImg from "../../../../assets/images/text-icon.png"; 6 | import audioPreviewImg from "../../../../assets/images/music-icon.png"; 7 | import apkPreviewImg from "../../../../assets/images/apk-icon.png"; 8 | import zipPreviewImg from "../../../../assets/images/zip-icon.png"; 9 | import sqlPreviewImg from "../../../../assets/images/sql-icon.png"; 10 | import filePreviewImg from "../../../../assets/images/file-icon.png"; 11 | import { StaticImageData } from "next/image"; 12 | 13 | export default function SingleFileUpload({ 14 | accept, 15 | uploadedFile, 16 | callback, 17 | uploadBtnText = "Upload", 18 | progressBtnText = "Uploading...", 19 | pdfPreview = pdfPreviewImg, 20 | textPreview = textPreviewImg, 21 | audioPreview = audioPreviewImg, 22 | apkPreview = apkPreviewImg, 23 | zipPreview = zipPreviewImg, 24 | sqlPreview = sqlPreviewImg, 25 | filePreview = filePreviewImg, 26 | children, 27 | }: any) { 28 | const [previewFileData, setPreviewFileData] = uploadedFile; 29 | const [isUploading, setIsUploading] = useState(false); 30 | const [fileObj, setFileObj] = useState(null); 31 | 32 | const fileData = {} as { 33 | previewType: string; 34 | previewUrl: string | StaticImageData | ArrayBuffer | null; 35 | previewName: string; 36 | isDragging: boolean; 37 | isLoading: boolean; 38 | }; 39 | 40 | const inputRef = useRef(null); 41 | 42 | const selectFile = () => { 43 | if (isUploading) { 44 | return; 45 | } 46 | if (inputRef.current) { 47 | (inputRef.current as HTMLInputElement).click(); 48 | } 49 | }; 50 | 51 | const handleFileChange = (event: any) => { 52 | const file = event.target.files[0]; 53 | if (file) { 54 | previewFile(file); 55 | } 56 | }; 57 | 58 | const previewFile = (file: any) => { 59 | fileData.previewType = "image"; 60 | const reader = new FileReader(); 61 | 62 | reader.onload = () => { 63 | if (file.type.startsWith("image/")) { 64 | fileData.previewUrl = reader.result; 65 | } else if (file.type === "text/plain") { 66 | fileData.previewUrl = textPreview; 67 | } else if (file.type === "application/pdf") { 68 | fileData.previewUrl = pdfPreview; 69 | } else if (file.type.startsWith("video/")) { 70 | fileData.previewType = "video"; 71 | fileData.previewUrl = URL.createObjectURL(file); 72 | } else if (file.type.startsWith("audio/")) { 73 | fileData.previewUrl = audioPreview; 74 | } else if (file.type === "application/vnd.android.package-archive") { 75 | fileData.previewUrl = apkPreview; 76 | } else if (file.type === "application/zip") { 77 | fileData.previewUrl = zipPreview; 78 | } else if (file.type === "application/sql") { 79 | fileData.previewUrl = sqlPreview; 80 | } else { 81 | fileData.previewUrl = filePreview; 82 | } 83 | fileData.previewName = file.name; 84 | setFileObj(file); 85 | setPreviewFileData(fileData); 86 | }; 87 | reader.onerror = (error) => { 88 | console.error(`Error while reading file ${file.name}: ${error}`); 89 | }; 90 | reader.readAsDataURL(file); 91 | }; 92 | 93 | const uploadingFunction = async () => { 94 | if (isUploading) { 95 | return; 96 | } 97 | setIsUploading(true); 98 | 99 | await callback(fileObj); 100 | setFileObj(previewFileData); 101 | 102 | setIsUploading(false); 103 | }; 104 | 105 | const handleDragOver = (event: any) => { 106 | if (isUploading) { 107 | return; 108 | } 109 | event.preventDefault(); 110 | fileData.isDragging = true; 111 | }; 112 | 113 | const handleDragLeave = (event: any) => { 114 | event.preventDefault(); 115 | fileData.isDragging = false; 116 | }; 117 | 118 | const handleDrop = (event: any) => { 119 | if (isUploading) { 120 | return; 121 | } 122 | event.preventDefault(); 123 | fileData.isDragging = false; 124 | 125 | const file = event.dataTransfer.files[0]; 126 | if (file && (accept ? accept.split(", ").includes(file.type) : true)) { 127 | previewFile(file); 128 | } 129 | }; 130 | 131 | return ( 132 |
133 |
140 | {children} 141 | 148 |
149 |
156 | 168 |
169 |
170 | ); 171 | } 172 | -------------------------------------------------------------------------------- /react/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/react/src/app/favicon.ico -------------------------------------------------------------------------------- /react/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /react/src/app/index.ts: -------------------------------------------------------------------------------- 1 | import "../../dist/css/output.css"; 2 | import "../../dist/css/style.scss"; 3 | 4 | import SingleFileUpload from "./components/singleFileUpload.tsx"; 5 | import MultipleFileUpload from "./components/multipleFileUpload.tsx"; 6 | 7 | export { SingleFileUpload, MultipleFileUpload }; 8 | -------------------------------------------------------------------------------- /react/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import "../../dist/style.css"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "React file upload management", 10 | description: 11 | "A file management system, allows for single and multiple file uploading with a preview feature", 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: { 17 | children: React.ReactNode; 18 | }) { 19 | return ( 20 | 21 | {children} 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /react/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image, { StaticImageData } from "next/image"; 4 | import React, { useState } from "react"; 5 | import SingleFileUpload from "./components/singleFileUpload"; 6 | import MultipleFileUpload from "./components/multipleFileUpload"; 7 | import img from "../../../assets/images/example-img.jpg"; 8 | import placeHolderImg from "../../../assets/images/placeholder.png"; 9 | 10 | export default function App() { 11 | const [previewFileData, setPreviewFileData] = useState( 12 | {} as { 13 | previewType: string; 14 | previewUrl: string | StaticImageData | ArrayBuffer | null; 15 | previewName: string; 16 | isDragging: boolean; 17 | } 18 | ); 19 | 20 | const handleFileUploading = async (file: any) => { 21 | await new Promise((resolve) => setTimeout(resolve, 2000)); 22 | setPreviewFileData({ 23 | previewType: "image", 24 | previewUrl: img, 25 | previewName: file.name, 26 | isDragging: false, 27 | }); 28 | }; 29 | 30 | const uploadedFiles = [] as Array<{ 31 | fileType: string; 32 | fileUrl: string | StaticImageData; 33 | fileName: string; 34 | }>; 35 | 36 | const handleFilesUploading = async (files: any) => { 37 | const uploadedFiles = []; 38 | 39 | for (var i = 0; i < files.length; i++) { 40 | uploadedFiles.push({ 41 | fileType: "image", 42 | fileUrl: img, 43 | fileName: files[i].name, 44 | }); 45 | } 46 | 47 | await new Promise((resolve) => setTimeout(resolve, 5000)); 48 | return uploadedFiles; 49 | }; 50 | 51 | return ( 52 |
53 |

Single File Upload

54 | 60 |
61 |
62 | {!previewFileData || !previewFileData.previewUrl ? ( 63 | 89 | ) : ( 90 |
91 | {previewFileData.previewType != "video" ? ( 92 | image 99 | ) : ( 100 | 110 | )} 111 |
112 | )} 113 |
114 |

115 | {previewFileData ? previewFileData.previewName : ""} 116 |

117 |
118 |
119 |
120 |
121 |

Multiple File Upload

122 | 129 | {(file: any) => ( 130 |
131 |
132 | {!file.previewUrl ? ( 133 | 159 | ) : ( 160 |
161 | {file.previewType != "video" ? ( 162 | image 169 | ) : ( 170 | 180 | )} 181 |
182 | )} 183 |
184 |

{file ? file.previewName : ""}

185 |
186 | )} 187 |
188 |

Single File Upload

189 | 195 |
196 |
197 |
198 | {previewFileData.previewType != "video" ? ( 199 | image 210 | ) : ( 211 | 221 | )} 222 |
223 |

224 | {previewFileData.previewName 225 | ? previewFileData.previewName 226 | : "Click to upload or drag and drop files"} 227 |

228 |
229 |
230 |
231 |
232 |
233 |

Multiple File Upload

234 | 241 | {(file: any) => ( 242 |
243 |
244 |
245 | {file.previewType != "video" ? ( 246 | image 257 | ) : ( 258 | 265 | )} 266 |
267 |

268 | {file.previewName 269 | ? file.previewName 270 | : "Click to upload or drag and drop files"} 271 |

272 |
273 |
274 | )} 275 |
276 |

Single File Upload

277 | 283 |
284 |
285 |
286 | {previewFileData.previewType != "video" ? ( 287 | image 298 | ) : ( 299 | 309 | )} 310 |
311 |
312 |

313 | {previewFileData.previewName} 314 |

315 |
316 |
317 |
318 |
319 |

Multiple File Upload

320 | 327 | {(file: any) => ( 328 |
329 |
330 |
331 | {file.previewType != "video" ? ( 332 | image 343 | ) : ( 344 | 351 | )} 352 |
353 |
354 |

355 | {file.previewName} 356 |

357 |
358 | )} 359 |
360 |

Single File Upload

361 | 367 |
368 |
369 |
370 | {previewFileData.previewType != "video" ? ( 371 | image 382 | ) : ( 383 | 393 | )} 394 |
395 |
396 |

397 | {previewFileData.previewName} 398 |

399 |
400 |
401 |
402 |
403 |

Multiple File Upload

404 | 411 | {(file: any) => ( 412 |
413 |
414 |
415 | {file.previewType != "video" ? ( 416 | image 427 | ) : ( 428 | 435 | )} 436 |
437 |
438 |

439 | {file.previewName} 440 |

441 |
442 | )} 443 |
444 |
445 | ); 446 | } 447 | -------------------------------------------------------------------------------- /react/src/app/style.scss: -------------------------------------------------------------------------------- 1 | @mixin gradient-striped($color: rgba(255, 255, 255, 0.15), $angle: 45deg) { 2 | background-image: -webkit-linear-gradient( 3 | $angle, 4 | $color 25%, 5 | transparent 25%, 6 | transparent 50%, 7 | $color 50%, 8 | $color 75%, 9 | transparent 75%, 10 | transparent 11 | ); 12 | background-image: -o-linear-gradient( 13 | $angle, 14 | $color 25%, 15 | transparent 25%, 16 | transparent 50%, 17 | $color 50%, 18 | $color 75%, 19 | transparent 75%, 20 | transparent 21 | ); 22 | background-image: linear-gradient( 23 | $angle, 24 | $color 25%, 25 | transparent 25%, 26 | transparent 50%, 27 | $color 50%, 28 | $color 75%, 29 | transparent 75%, 30 | transparent 31 | ); 32 | } 33 | 34 | @-webkit-keyframes progress-bar-stripes { 35 | from { 36 | background-position: 40px 0; 37 | } 38 | to { 39 | background-position: 0 0; 40 | } 41 | } 42 | 43 | @keyframes progress-bar-stripes { 44 | from { 45 | background-position: 40px 0; 46 | } 47 | to { 48 | background-position: 0 0; 49 | } 50 | } 51 | 52 | @mixin animation($animation) { 53 | -webkit-animation: $animation; 54 | -o-animation: $animation; 55 | animation: $animation; 56 | } 57 | .progress.active .progress-bar { 58 | @include animation(progress-bar-stripes 2s linear infinite); 59 | } 60 | .progress-striped .progress-bar { 61 | @include gradient-striped; 62 | background-size: 40px 40px; 63 | } 64 | -------------------------------------------------------------------------------- /react/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "target": "es5", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": false, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "bundler", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "allowImportingTsExtensions": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "plugins": [ 19 | { 20 | "name": "next" 21 | } 22 | ], 23 | "paths": { 24 | "@/*": ["./src/*"] 25 | } 26 | }, 27 | "include": [ 28 | "next-env.d.ts", 29 | "**/*.ts", 30 | "**/*.tsx", 31 | ".next/cache/types/**/*.ts", 32 | ".next/types/**/*.ts", 33 | "./src", 34 | ".next/types/**/*.ts" 35 | ], 36 | "exclude": ["node_modules"] 37 | } 38 | -------------------------------------------------------------------------------- /react/vite.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | import { defineConfig } from "vite"; 3 | 4 | export default defineConfig({ 5 | build: { 6 | lib: { 7 | entry: resolve(__dirname, "./src/app/index.ts"), 8 | name: "@canopassoftware/react-file-upload", 9 | fileName: "index", 10 | }, 11 | rollupOptions: { 12 | external: ["react"], 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /vue/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ] 11 | ], 12 | "plugins": [ 13 | "@babel/plugin-proposal-class-properties", 14 | "@babel/plugin-proposal-object-rest-spread" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /vue/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | '@vue/eslint-config-prettier/skip-formatting' 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 'latest' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vue/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | .vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | index.mjs 31 | index.umd.js 32 | favicon.ico 33 | style.css -------------------------------------------------------------------------------- /vue/.npmignore: -------------------------------------------------------------------------------- 1 | /* 2 | !/dist -------------------------------------------------------------------------------- /vue/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /vue/README.md: -------------------------------------------------------------------------------- 1 | # Vue File Management with Preview - Fully Customized 2 | 3 | Vue file management system built with Vue.js and TypeScript that allows for single and multiple file uploading with a preview feature. It allows you to select files and preview them, returning an array of selected files. It allows customizing design by overriding the style classes. 4 | 5 | --- 6 | 7 | ## Table of Contents 8 | 9 | - [Getting Started](#getting-started) 10 | - [Prerequisites](#prerequisites) 11 | - [Installation](#installation) 12 | - [Properties and Events](#properties-and-events) 13 | - [Usage](#usage) 14 | 15 | --- 16 | 17 | ## Getting Started 18 | 19 | Follow below instructions to configure this package into your project. 20 | 21 | ### Prerequisites 22 | 23 | Before you begin, make sure you have the following software installed: 24 | 25 | - [Node.js](https://nodejs.org/) - v20.x 26 | - Vue3 27 | 28 | ### Installation 29 | 30 | Using npm: 31 | 32 | ``` 33 | npm install @canopassoftware/vue-file-upload 34 | ``` 35 | 36 | Using yarn: 37 | 38 | ``` 39 | yarn add @canopassoftware/vue-file-upload 40 | ``` 41 | 42 | --- 43 | 44 | ## Properties and Events 45 | 46 | ### props 47 | 48 | - **:callback="handleFileUploading"** 49 | 50 | - `required` 51 | - **Description:** Add your upload callback function while receive the selected file/files 52 | 53 | - **:uploadedFile="uploadedFile"** - For single file component 54 | 55 | - `required` 56 | - Uploaded file object with below format, 57 | ``` 58 | { 59 | fileType: string, 60 | fileUrl: string, 61 | fileName: string 62 | } 63 | ``` 64 | 65 | - **:uploadedFiles="uploadedFiles"** - For multiple file component 66 | 67 | - `required` 68 | - Uploaded files array with below format, 69 | ``` 70 | [ 71 | { 72 | fileType: string, 73 | fileUrl: string, 74 | fileName: string 75 | } 76 | ] 77 | ``` 78 | 79 | - **:uploadBtnText="'Upload'"** 80 | 81 | - **default** : Upload 82 | - Text for save or upload file button 83 | 84 | - **:progressBtnText="'Uploading...'"** 85 | 86 | - **default** : Uploading... 87 | - Text for the progress bar, showing file uploading under the process 88 | 89 | - **:removeBtnText="'Uploading...'"** 90 | 91 | - **default** : x 92 | - Text for remove file button 93 | 94 | - **:accept="'image/jpg, image/jpeg, image/png, video/mp4, audio/mp3, application/zip'"** 95 | 96 | - Validation for file type. By default it will select all the type of file. 97 | 98 | - **:(filetype)Preview="'(file location)'"** 99 | - **default** : Default file icons as per file types 100 | - Set path for your customized icon if needed 101 | 102 | --- 103 | 104 | ## Usage 105 | 106 | To manage and preview files with this library, follow these steps: 107 | 108 | ### Import the library into your file 109 | 110 | ```js 111 | // using CommonJS 112 | const { SingleFileUpload, MultipleFileUpload } = require('@canopassoftware/vue-file-upload'); 113 | 114 | OR 115 | // using esModules 116 | import { SingleFileUpload, MultipleFileUpload } from '@canopassoftware/vue-file-upload'; 117 | ``` 118 | 119 | ### Use default CSS 120 | 121 | - Use `style.css` for UI by importing like, 122 | 123 | ```js 124 | import '@canopassoftware/vue-file-upload/style.css'; 125 | ``` 126 | 127 | ### Creating custom UI with file preview 128 | 129 | - You can customize file uploading UI in `template` block. 130 | - The `file` slot containing `file` object with following keys, we will use this object to show preview. 131 | 132 | ```js 133 | file = file: { 134 | previewType: 'video', // type of the preview. like, file is image or video 135 | previewUrl: 'data:image/jpeg;base64,/9j/D1AAAACRsdW1pAAAD...', // URL of the file preview 136 | previewName: 'a152148640581.62d918f12a0b4.mp4', // preview file name 137 | isDragging: false // you will get it `true` when you dragging the file on design 138 | } 139 | ``` 140 | 141 | ### Single File Upload Management 142 | 143 | ```html 144 |
145 | 160 | 163 | 164 |
165 | ``` 166 | 167 | ```js 168 | 196 | ``` 197 | 198 | ### Multiple File Upload Management 199 | 200 | ```html 201 |
202 | 217 | 220 | 221 |
222 | ``` 223 | 224 | ```js 225 | 258 | ``` 259 | -------------------------------------------------------------------------------- /vue/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /vue/examples/CanvasView.vue: -------------------------------------------------------------------------------- 1 | 118 | 119 | 168 | -------------------------------------------------------------------------------- /vue/examples/LongSquareView.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 148 | -------------------------------------------------------------------------------- /vue/examples/RoundView.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 161 | -------------------------------------------------------------------------------- /vue/examples/SquareView.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 161 | -------------------------------------------------------------------------------- /vue/examples/demo/README.md: -------------------------------------------------------------------------------- 1 | # test-vue-file-upload 2 | 3 | This ready-to-use example shows real-time usage of the `vue-file-upload` library with custom UI. 4 | 5 | ## Customize configuration 6 | 7 | See [Vite Configuration Reference](https://vitejs.dev/config/). 8 | 9 | ## Project Setup 10 | 11 | ```sh 12 | npm install 13 | ``` 14 | 15 | ### Compile and Hot-Reload for Development 16 | 17 | ```sh 18 | npm run dev 19 | ``` 20 | 21 | ### Type-Check, Compile and Minify for Production 22 | 23 | ```sh 24 | npm run build 25 | ``` 26 | 27 | ### Lint with [ESLint](https://eslint.org/) 28 | 29 | ```sh 30 | npm run lint 31 | ``` 32 | -------------------------------------------------------------------------------- /vue/examples/demo/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /vue/examples/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /vue/examples/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-vue-file-upload", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "run-p type-check \"build-only {@}\" --", 8 | "preview": "vite preview", 9 | "build-only": "vite build", 10 | "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false", 11 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 12 | "format": "prettier --write src/" 13 | }, 14 | "dependencies": { 15 | "@canopassoftware/vue-file-upload": "^1.0.8", 16 | "vue": "^3.3.4", 17 | "vue-router": "^4.2.5" 18 | }, 19 | "devDependencies": { 20 | "@rushstack/eslint-patch": "^1.3.3", 21 | "@tsconfig/node18": "^18.2.2", 22 | "@types/node": "^18.18.5", 23 | "@vitejs/plugin-vue": "^4.4.0", 24 | "@vue/eslint-config-prettier": "^8.0.0", 25 | "@vue/eslint-config-typescript": "^12.0.0", 26 | "@vue/tsconfig": "^0.4.0", 27 | "eslint": "^8.49.0", 28 | "eslint-plugin-vue": "^9.17.0", 29 | "npm-run-all2": "^6.1.1", 30 | "prettier": "^3.0.3", 31 | "typescript": "~5.2.0", 32 | "vite": "^4.4.11", 33 | "vue-tsc": "^1.8.19" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vue/examples/demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/vue/examples/demo/public/favicon.ico -------------------------------------------------------------------------------- /vue/examples/demo/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /vue/examples/demo/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /vue/examples/demo/src/assets/main.css: -------------------------------------------------------------------------------- 1 | #app { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | 6 | font-weight: normal; 7 | } 8 | 9 | a, 10 | .green { 11 | text-decoration: none; 12 | color: hsla(160, 100%, 37%, 1); 13 | transition: 0.4s; 14 | } 15 | 16 | @media (hover: hover) { 17 | a:hover { 18 | background-color: hsla(160, 100%, 37%, 0.2); 19 | } 20 | } 21 | 22 | @media (min-width: 1024px) { 23 | body { 24 | display: flex; 25 | place-items: center; 26 | } 27 | 28 | #app { 29 | display: grid; 30 | grid-template-columns: 1fr 1fr; 31 | padding: 0 2rem; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vue/examples/demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | 5 | import "@canopassoftware/vue-file-upload/style.css" 6 | import { SingleFileUpload, MultipleFileUpload } from '@canopassoftware/vue-file-upload' 7 | 8 | const app = createApp(App) 9 | 10 | app.component('SingleFileUpload', SingleFileUpload) 11 | app.component('MultipleFileUpload', MultipleFileUpload) 12 | 13 | app.use(router) 14 | 15 | app.mount('#app') 16 | -------------------------------------------------------------------------------- /vue/examples/demo/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import IndexView from '../views/IndexView.vue' 3 | 4 | const router = createRouter({ 5 | history: createWebHistory(import.meta.env.BASE_URL), 6 | routes: [ 7 | { 8 | path: '/', 9 | name: 'IndexView', 10 | component: IndexView 11 | } 12 | ] 13 | }) 14 | 15 | export default router 16 | -------------------------------------------------------------------------------- /vue/examples/demo/src/views/IndexView.vue: -------------------------------------------------------------------------------- 1 | 121 | 122 | 165 | -------------------------------------------------------------------------------- /vue/examples/demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "baseUrl": ".", 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vue/examples/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /vue/examples/demo/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Bundler", 14 | "types": ["node"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vue/examples/demo/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | vue(), 10 | ], 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)) 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Manage file/image uploading 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@canopassoftware/vue-file-upload", 3 | "version": "1.0.8", 4 | "description": "Show the preview of file and manage files data to upload", 5 | "main": "index.umd.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/canopas/file-upload-web-frontend.git" 9 | }, 10 | "keywords": [ 11 | "file-upload", 12 | "image-upload", 13 | "file-management", 14 | "file-preview", 15 | "image-preview" 16 | ], 17 | "author": "dharti", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/canopas/file-upload-web-frontend/issues" 21 | }, 22 | "homepage": "https://github.com/canopas/file-upload-web-frontend#readme", 23 | "scripts": { 24 | "dev": "tailwindcss -i ./src/input.css -o ./dist/output.css && vite", 25 | "build": "npm run build-css && vite build && cp -r ./dist/* ./", 26 | "preview": "vite preview", 27 | "build-only": "vite build", 28 | "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false", 29 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 30 | "format": "prettier --write src/", 31 | "test": "echo \"Error: no test specified\" && exit 1", 32 | "build-css": "tailwindcss -i ./src/input.css -o ./dist/output.css --minify && cp ./src/style.scss ./dist/style.scss" 33 | }, 34 | "dependencies": { 35 | "vue": "^3.3.4", 36 | "vue-router": "^4.2.4" 37 | }, 38 | "devDependencies": { 39 | "@babel/cli": "^7.23.0", 40 | "@babel/core": "^7.23.2", 41 | "@babel/preset-env": "^7.23.2", 42 | "@rushstack/eslint-patch": "^1.3.3", 43 | "@tsconfig/node18": "^18.2.2", 44 | "@types/node": "^18.17.17", 45 | "@vitejs/plugin-vue": "^4.3.4", 46 | "@vue/eslint-config-prettier": "^8.0.0", 47 | "@vue/eslint-config-typescript": "^12.0.0", 48 | "@vue/tsconfig": "^0.4.0", 49 | "autoprefixer": "^10.4.16", 50 | "eslint": "^8.49.0", 51 | "eslint-plugin-vue": "^9.17.0", 52 | "npm-run-all2": "^6.0.6", 53 | "prettier": "^3.0.3", 54 | "prettier-plugin-tailwindcss": "^0.5.6", 55 | "sass": "^1.69.4", 56 | "tailwindcss": "^3.3.3", 57 | "typescript": "~5.2.0", 58 | "vite": "^4.4.9", 59 | "vue-tsc": "^1.8.11" 60 | }, 61 | "directories": { 62 | "example": "examples" 63 | }, 64 | "files": [ 65 | "./index.mjs", 66 | "./index.umd.js", 67 | "./style.css" 68 | ] 69 | } -------------------------------------------------------------------------------- /vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canopas/web-file-upload/ebdf242a69b84ba03d6778db87e3b2e064d3c933/vue/public/favicon.ico -------------------------------------------------------------------------------- /vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /vue/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /vue/src/components/MultipleFile.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 335 | -------------------------------------------------------------------------------- /vue/src/components/SingleFile.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 211 | -------------------------------------------------------------------------------- /vue/src/index.ts: -------------------------------------------------------------------------------- 1 | import '../dist/output.css' 2 | import '../dist/style.scss' 3 | 4 | import SingleFileUpload from './components/SingleFile.vue' 5 | import MultipleFileUpload from './components/MultipleFile.vue' 6 | 7 | import { createApp } from 'vue' 8 | import App from './App.vue' 9 | import router from './router' 10 | 11 | const app = createApp(App) 12 | 13 | app.component('SingleFileUpload', SingleFileUpload) 14 | app.component('MultipleFileUpload', MultipleFileUpload) 15 | 16 | app.use(router) 17 | 18 | app.mount('#app') 19 | 20 | export { SingleFileUpload, MultipleFileUpload } 21 | -------------------------------------------------------------------------------- /vue/src/input.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /vue/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import IndexView from '../views/IndexView.vue' 3 | 4 | const router = createRouter({ 5 | history: createWebHistory(import.meta.env.BASE_URL), 6 | routes: [ 7 | { 8 | path: '/', 9 | name: 'IndexView', 10 | component: IndexView 11 | } 12 | ] 13 | }) 14 | 15 | export default router 16 | -------------------------------------------------------------------------------- /vue/src/style.scss: -------------------------------------------------------------------------------- 1 | @mixin gradient-striped($color: rgba(255, 255, 255, 0.15), $angle: 45deg) { 2 | background-image: -webkit-linear-gradient( 3 | $angle, 4 | $color 25%, 5 | transparent 25%, 6 | transparent 50%, 7 | $color 50%, 8 | $color 75%, 9 | transparent 75%, 10 | transparent 11 | ); 12 | background-image: -o-linear-gradient( 13 | $angle, 14 | $color 25%, 15 | transparent 25%, 16 | transparent 50%, 17 | $color 50%, 18 | $color 75%, 19 | transparent 75%, 20 | transparent 21 | ); 22 | background-image: linear-gradient( 23 | $angle, 24 | $color 25%, 25 | transparent 25%, 26 | transparent 50%, 27 | $color 50%, 28 | $color 75%, 29 | transparent 75%, 30 | transparent 31 | ); 32 | } 33 | 34 | @-webkit-keyframes progress-bar-stripes { 35 | from { 36 | background-position: 40px 0; 37 | } 38 | to { 39 | background-position: 0 0; 40 | } 41 | } 42 | 43 | @keyframes progress-bar-stripes { 44 | from { 45 | background-position: 40px 0; 46 | } 47 | to { 48 | background-position: 0 0; 49 | } 50 | } 51 | 52 | @mixin animation($animation) { 53 | -webkit-animation: $animation; 54 | -o-animation: $animation; 55 | animation: $animation; 56 | } 57 | .progress.active .progress-bar { 58 | @include animation(progress-bar-stripes 2s linear infinite); 59 | } 60 | .progress-striped .progress-bar { 61 | @include gradient-striped; 62 | background-size: 40px 40px; 63 | } 64 | -------------------------------------------------------------------------------- /vue/src/views/IndexView.vue: -------------------------------------------------------------------------------- 1 | 97 | 98 | 148 | -------------------------------------------------------------------------------- /vue/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | variants: { 8 | extend: {} 9 | }, 10 | plugins: [] 11 | } 12 | -------------------------------------------------------------------------------- /vue/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "baseUrl": ".", 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /vue/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "module": "ESNext", 13 | "strict": true, 14 | "moduleResolution": "Bundler", 15 | } 16 | } -------------------------------------------------------------------------------- /vue/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { resolve } from "path"; 3 | import vue from "@vitejs/plugin-vue"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | build: { 9 | lib: { 10 | // src/indext.ts is where we have exported the component(s) 11 | entry: resolve(__dirname, "src/index.ts"), 12 | name: "@canopassoftware/vue-file-upload", 13 | // the name of the output files when the build is run 14 | fileName: "index", 15 | }, 16 | rollupOptions: { 17 | // make sure to externalize deps that shouldn't be bundled 18 | // into your library 19 | external: ["vue"], 20 | output: { 21 | // Provide global variables to use in the UMD build 22 | // for externalized deps 23 | globals: { 24 | vue: "Vue", 25 | }, 26 | }, 27 | }, 28 | }, 29 | }); --------------------------------------------------------------------------------