Repository: thecodejack/svelte-file-dropzone Branch: master Commit: cedd55fd2ad0 Files: 44 Total size: 38.9 KB Directory structure: gitextract_nknodkyk/ ├── .gitignore ├── .prettierrc ├── .storybook/ │ ├── main.js │ └── preview-head.html ├── README.md ├── TODO ├── demo/ │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── package.json │ ├── src/ │ │ ├── app.d.ts │ │ ├── app.html │ │ ├── index.test.ts │ │ ├── lib/ │ │ │ └── index.ts │ │ └── routes/ │ │ ├── basic/ │ │ │ └── +page.svelte │ │ ├── custom-props/ │ │ │ └── +page.svelte │ │ ├── form/ │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ ├── reactive-disabled/ │ │ │ └── +page.svelte │ │ └── toggle-multiple/ │ │ └── +page.svelte │ ├── svelte.config.js │ ├── tsconfig.json │ └── vite.config.ts ├── jsconfig.json ├── package.json ├── renovate.json ├── rollup.config.js ├── src/ │ └── lib/ │ ├── components/ │ │ └── Dropzone.svelte │ ├── index.ts │ └── utils/ │ ├── attr-accept.js │ └── index.js └── stories/ ├── 1-dropzone.stories.js ├── 2-advanced-dropzone.stories.js ├── helpers.js └── views/ ├── BasicDropzoneAcceptImagesView.svelte ├── BasicDropzoneView.svelte ├── CustomSlotDropzoneView.svelte ├── DisabledDropzoneView.svelte ├── FullyFeaturedDropzoneView.svelte ├── NoClickDropzoneView.svelte ├── NoDragDropzoneView.svelte └── WorkingCSVFileUploadView.svelte ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules dist storybook-static package-lock.json .DS_Store .svelte-kit ================================================ FILE: .prettierrc ================================================ { "useTabs": false, "singleQuote": false, "trailingComma": "none", "printWidth": 100, "plugins": ["prettier-plugin-svelte"], "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }], "arrowParens": "avoid" } ================================================ FILE: .storybook/main.js ================================================ export default { stories: ["../stories/**/*.stories.js"], addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions"], framework: { name: "@storybook/sveltekit", options: {}, }, docs: { autodocs: true, }, }; ================================================ FILE: .storybook/preview-head.html ================================================ ================================================ FILE: README.md ================================================ # svelte-file-dropzone [![NPM](https://img.shields.io/npm/v/svelte-file-dropzone.svg)](https://www.npmjs.com/package/svelte-file-dropzone) [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/svelte-file-dropzone.svg)](https://www.npmjs.com/package/svelte-file-dropzone) SvelteJS component for file upload and dropzone.The component is Svelte implementation of [react-dropzone](https://github.com/react-dropzone/react-dropzone). ## Demo [Click here for Storybook link](https://svelte-file-dropzone.netlify.app/?path=/info/examples--basic-dropzone) ## Installation ``` npm install svelte-file-dropzone or yarn add svelte-file-dropzone ``` ## Usage ```svelte
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
``` ## API ### Props | Prop Name | Description | Default Value | | --------------------- | ---------------------------------------------------------------------------------------- | ------------- | | accept | Set accepted file types. See https://github.com/okonet/attr-accept for more information. | undefined | | disabled | | false | | maxSize | | Infinity | | minSize | | 0 | | multiple | if true, multiple files can be selected at once | true | | preventDropOnDocument | 1231 | true | | noClick | disable click events | false | | noKeyboard | disable keyboard events | false | | noDrag | disable drag events | false | | containerClasses | custom container classes | "" | | containerStyles | custom inline container styles | "" | | disableDefaultStyles | don't apply default styles to container | false | | inputElement | reference to inputElement | undefined | | required | html5 required attribute added to input | false | ### Events | Event Name | Description | `event.detail` info | | ---------------- | ----------- | -------------------------------------- | | dragenter | | `{dragEvent: event}` | | dragover | | `{dragEvent: event}` | | dragleave | | `{dragEvent: event}` | | drop | | `{acceptedFiles,fileRejections,event}` | | filedropped | | `{event}` | | droprejected | | `{fileRejections,event}` | | dropaccepted | | `{acceptedFiles,event}` | | filedialogcancel | | | ### Examples [Click here](https://github.com/thecodejack/svelte-file-dropzone/tree/master/stories/views) to view stories implementation ## Credits Component is reimplementation [react-dropzone](https://github.com/react-dropzone/react-dropzone). Complete credit goes to author and contributors of [react-dropzone](https://github.com/react-dropzone/react-dropzone). ## License MIT ================================================ FILE: TODO ================================================ Todo: ✔ Git repo @done(20-06-17 21:58) ✔ npm initial publish @done(20-06-18 22:34) ✔ setup netlify @done(20-06-17 22:55) ✔ Fix README.md @done(20-06-17 22:28) ✔ Add README.md to Notes of storybook @done(20-06-18 22:34) ✔ Build setup @done(20-06-17 22:33) ☐ Advanced Examples ☐ Custom Dropzone with delete file etc ✔ CustomDropzone with different drop behaviour @done(20-06-18 22:34) ================================================ FILE: demo/.gitignore ================================================ .DS_Store node_modules /build /.svelte-kit /package .env .env.* !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* ================================================ FILE: demo/.npmrc ================================================ engine-strict=true ================================================ FILE: demo/.prettierignore ================================================ # Ignore files for PNPM, NPM and YARN pnpm-lock.yaml package-lock.json yarn.lock ================================================ FILE: demo/.prettierrc ================================================ { "useTabs": true, "singleQuote": true, "trailingComma": "none", "printWidth": 100, "plugins": ["prettier-plugin-svelte"], "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] } ================================================ FILE: demo/README.md ================================================ # create-svelte Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). ## Creating a project If you're seeing this, you've probably already done this step. Congrats! ```bash # create a new project in the current directory npm create svelte@latest # create a new project in my-app npm create svelte@latest my-app ``` ## Developing Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: ```bash npm run dev # or start the server and open the app in a new browser tab npm run dev -- --open ``` ## Building To create a production version of your app: ```bash npm run build ``` You can preview the production build with `npm run preview`. > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. ================================================ FILE: demo/package.json ================================================ { "name": "demo", "version": "0.0.1", "private": true, "scripts": { "dev": "vite dev", "build": "vite build", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "test": "vitest", "lint": "prettier --check .", "format": "prettier --write ." }, "devDependencies": { "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.5.5", "@sveltejs/vite-plugin-svelte": "^3.0.0", "prettier": "^3.1.1", "prettier-plugin-svelte": "^3.1.2", "svelte": "^4.2.12", "svelte-check": "^3.6.0", "tslib": "^2.4.1", "typescript": "^5.0.0", "vite": "^5.0.3", "vitest": "^1.0.0" }, "type": "module" } ================================================ FILE: demo/src/app.d.ts ================================================ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { namespace App { // interface Error {} // interface Locals {} // interface PageData {} // interface PageState {} // interface Platform {} } } export {}; ================================================ FILE: demo/src/app.html ================================================ %sveltekit.head%
%sveltekit.body%
================================================ FILE: demo/src/index.test.ts ================================================ import { describe, it, expect } from 'vitest'; describe('sum test', () => { it('adds 1 + 2 to equal 3', () => { expect(1 + 2).toBe(3); }); }); ================================================ FILE: demo/src/lib/index.ts ================================================ // place files you want to import through the `$lib` alias in this folder. ================================================ FILE: demo/src/routes/basic/+page.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: demo/src/routes/custom-props/+page.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: demo/src/routes/form/+page.server.ts ================================================ export const actions = { async postFiles({ request }) { const formData: FormData = await request.formData(); const files = formData.getAll('files'); return { files: files.map((f) => (f as any).name) }; } }; ================================================ FILE: demo/src/routes/form/+page.svelte ================================================
Files about to upload:
    {#each files as file}
  • {file.name}
  • {/each}

Files posted to form action:
    {#each $page.form?.files ?? [] as file}
  • {file}
  • {/each}
================================================ FILE: demo/src/routes/reactive-disabled/+page.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: demo/src/routes/toggle-multiple/+page.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: demo/svelte.config.js ================================================ import adapter from '@sveltejs/adapter-auto'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { // Consult https://kit.svelte.dev/docs/integrations#preprocessors // for more information about preprocessors preprocess: vitePreprocess(), kit: { // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. // If your environment is not supported or you settled on a specific environment, switch out the adapter. // See https://kit.svelte.dev/docs/adapters for more information about adapters. adapter: adapter() } }; export default config; ================================================ FILE: demo/tsconfig.json ================================================ { "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { "allowJs": true, "checkJs": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, "strict": true, "moduleResolution": "bundler" } // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias // // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes // from the referenced tsconfig.json - TypeScript does not merge them in } ================================================ FILE: demo/vite.config.ts ================================================ import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vitest/config'; export default defineConfig({ plugins: [sveltekit()], test: { include: ['src/**/*.{test,spec}.{js,ts}'] } }); ================================================ FILE: jsconfig.json ================================================ {} ================================================ FILE: package.json ================================================ { "name": "svelte-file-dropzone", "version": "2.0.9", "description": "Svelte component for fileupload and file dropzone", "scripts": { "package": "svelte-kit sync && svelte-package && publint", "prepublishOnly": "npm run package", "test": "echo \"Error: no test specified\" && exit 1", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, "exports": { ".": { "types": "./dist/index.d.ts", "svelte": "./dist/index.js" } }, "typesVersions": { ">4.0": { "Dropzone.svelte": [ "./dist/components/Dropzone.svelte.d.ts" ] } }, "repository": { "url": "https://github.com/thecodejack/svelte-file-dropzone" }, "author": "thecodejack", "license": "MIT", "dependencies": { "file-selector": "^0.6.0" }, "peerDependencies": { "svelte": "^3.54.0 || ^4.0.0 || ^5" }, "devDependencies": { "@storybook/addon-essentials": "^7.6.6", "@storybook/addon-interactions": "^7.6.6", "@storybook/addon-links": "^7.6.6", "@storybook/addons": "7.6.6", "@storybook/svelte": "7.6.6", "@storybook/sveltekit": "^7.6.6", "@sveltejs/kit": "^1.30.4", "@sveltejs/package": "^2.2.5", "babel-loader": "9.1.3", "publint": "^0.2.7", "storybook": "^7.6.6", "svelte": "^4.2.12", "vite": "^4.5.1" }, "keywords": [ "svelte", "svelte3", "svelte-components", "upload", "dropzone", "svelte-file-dropzone", "svelte-dropzone", "sveltejs" ], "files": [ "src", "dist", "!dist/**/*.test.*", "!dist/**/*.spec.*" ], "svelte": "./dist/index.js", "types": "./dist/index.d.ts", "type": "module" } ================================================ FILE: renovate.json ================================================ { "extends": [ "config:base" ] } ================================================ FILE: rollup.config.js ================================================ import svelte from "rollup-plugin-svelte"; import resolve from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; import pkg from "./package.json"; const name = pkg.name .replace(/^(@\S+\/)?(svelte-)?(\S+)/, "$3") .replace(/^\w/, (m) => m.toUpperCase()) .replace(/-\w/g, (m) => m[1].toUpperCase()); export default { input: "src/index.svelte", output: [ { file: pkg.module, format: "es" }, { file: pkg.main, format: "umd", name }, ], plugins: [svelte(), resolve(), commonjs()], }; ================================================ FILE: src/lib/components/Dropzone.svelte ================================================

{defaultPlaceholderString}

================================================ FILE: src/lib/index.ts ================================================ import Dropzone from "./components/Dropzone.svelte"; export default Dropzone ================================================ FILE: src/lib/utils/attr-accept.js ================================================ /** * Check if the provided file type should be accepted by the input with accept attribute. * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input#attr-accept * * Inspired by https://github.com/enyo/dropzone * * @param file {File} https://developer.mozilla.org/en-US/docs/Web/API/File * @param acceptedFiles {string} * @returns {boolean} */ export default function(file, acceptedFiles) { if (file && acceptedFiles) { const acceptedFilesArray = Array.isArray(acceptedFiles) ? acceptedFiles : acceptedFiles.split(","); const fileName = file.name || ""; const mimeType = (file.type || "").toLowerCase(); const baseMimeType = mimeType.replace(/\/.*$/, ""); return acceptedFilesArray.some((type) => { const validType = type.trim().toLowerCase(); if (validType.charAt(0) === ".") { return fileName.toLowerCase().endsWith(validType); } else if (validType.endsWith("/*")) { // This is something like a image/* mime type return baseMimeType === validType.replace(/\/.*$/, ""); } return mimeType === validType; }); } return true; } ================================================ FILE: src/lib/utils/index.js ================================================ import accepts from "./attr-accept"; // Error codes export const FILE_INVALID_TYPE = "file-invalid-type"; export const FILE_TOO_LARGE = "file-too-large"; export const FILE_TOO_SMALL = "file-too-small"; export const TOO_MANY_FILES = "too-many-files"; // File Errors export const getInvalidTypeRejectionErr = (accept) => { accept = Array.isArray(accept) && accept.length === 1 ? accept[0] : accept; const messageSuffix = Array.isArray(accept) ? `one of ${accept.join(", ")}` : accept; return { code: FILE_INVALID_TYPE, message: `File type must be ${messageSuffix}`, }; }; export const getTooLargeRejectionErr = (maxSize) => { return { code: FILE_TOO_LARGE, message: `File is larger than ${maxSize} bytes`, }; }; export const getTooSmallRejectionErr = (minSize) => { return { code: FILE_TOO_SMALL, message: `File is smaller than ${minSize} bytes`, }; }; export const TOO_MANY_FILES_REJECTION = { code: TOO_MANY_FILES, message: "Too many files", }; // Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with // that MIME type will always be accepted export function fileAccepted(file, accept) { const isAcceptable = file.type === "application/x-moz-file" || accepts(file, accept); return [ isAcceptable, isAcceptable ? null : getInvalidTypeRejectionErr(accept), ]; } export function fileMatchSize(file, minSize, maxSize) { if (isDefined(file.size)) { if (isDefined(minSize) && isDefined(maxSize)) { if (file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)]; if (file.size < minSize) return [false, getTooSmallRejectionErr(minSize)]; } else if (isDefined(minSize) && file.size < minSize) return [false, getTooSmallRejectionErr(minSize)]; else if (isDefined(maxSize) && file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)]; } return [true, null]; } function isDefined(value) { return value !== undefined && value !== null; } export function allFilesAccepted({ files, accept, minSize, maxSize, multiple, }) { if (!multiple && files.length > 1) { return false; } return files.every((file) => { const [accepted] = fileAccepted(file, accept); const [sizeMatch] = fileMatchSize(file, minSize, maxSize); return accepted && sizeMatch; }); } // React's synthetic events has event.isPropagationStopped, // but to remain compatibility with other libs (Preact) fall back // to check event.cancelBubble export function isPropagationStopped(event) { if (typeof event.isPropagationStopped === "function") { return event.isPropagationStopped(); } else if (typeof event.cancelBubble !== "undefined") { return event.cancelBubble; } return false; } export function isEvtWithFiles(event) { if (!event.dataTransfer) { return !!event.target && !!event.target.files; } // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file return Array.prototype.some.call( event.dataTransfer.types, (type) => type === "Files" || type === "application/x-moz-file" ); } export function isKindFile(item) { return typeof item === "object" && item !== null && item.kind === "file"; } function isIe(userAgent) { return ( userAgent.indexOf("MSIE") !== -1 || userAgent.indexOf("Trident/") !== -1 ); } function isEdge(userAgent) { return userAgent.indexOf("Edge/") !== -1; } export function isIeOrEdge(userAgent = window.navigator.userAgent) { return isIe(userAgent) || isEdge(userAgent); } /** * This is intended to be used to compose event handlers * They are executed in order until one of them calls `event.isPropagationStopped()`. * Note that the check is done on the first invoke too, * meaning that if propagation was stopped before invoking the fns, * no handlers will be executed. * * @param {Function} fns the event hanlder functions * @return {Function} the event handler to add to an element */ export function composeEventHandlers(...fns) { return (event, ...args) => fns.some((fn) => { if (!isPropagationStopped(event) && fn) { fn(event, ...args); } return isPropagationStopped(event); }); } ================================================ FILE: stories/1-dropzone.stories.js ================================================ //import { action } from "@storybook/addon-actions"; import BasicDropZoneView from "./views/BasicDropzoneView.svelte"; import BasicDropZoneViewSource from "./views/BasicDropzoneView.svelte?raw"; import DisabledDropzoneView from "./views/DisabledDropzoneView.svelte"; import DisabledDropzoneViewSource from "./views/DisabledDropzoneView.svelte?raw"; import CustomSlotDropzoneView from "./views/CustomSlotDropzoneView.svelte"; import CustomSlotDropzoneViewSource from "./views/CustomSlotDropzoneView.svelte?raw"; import NoClickDropZoneView from "./views/NoClickDropzoneView.svelte"; import NoClickDropZoneViewSource from "./views/NoClickDropzoneView.svelte?raw"; import NoDragDropzoneView from "./views/NoDragDropzoneView.svelte"; import NoDragDropzoneViewSource from "./views/NoDragDropzoneView.svelte?raw"; import { sourceParameters } from "./helpers"; export default { title: "Examples", component: null, }; export const BasicDropzone = { ...sourceParameters(BasicDropZoneViewSource), render: () => ({ Component: BasicDropZoneView, }), }; export const DisabledDropzone = { ...sourceParameters(DisabledDropzoneViewSource), render: () => ({ Component: DisabledDropzoneView, }), }; export const CustomSlotDropzone = { ...sourceParameters(CustomSlotDropzoneViewSource), render: () => ({ Component: CustomSlotDropzoneView, }), }; export const DropZoneWithClickDisabled = { ...sourceParameters(NoClickDropZoneViewSource), render: () => ({ Component: NoClickDropZoneView, }), }; export const DropZoneWithDragDisabled = { ...sourceParameters(NoDragDropzoneViewSource), render: () => ({ Component: NoDragDropzoneView, }), }; ================================================ FILE: stories/2-advanced-dropzone.stories.js ================================================ import { action } from "@storybook/addon-actions"; import WorkingCSVFileUploadView from "./views/WorkingCSVFileUploadView.svelte"; import WorkingCSVFileUploadViewSource from "./views/WorkingCSVFileUploadView.svelte?raw"; import FullyFeaturedDropzoneView from "./views/FullyFeaturedDropzoneView.svelte"; import FullyFeaturedDropzoneViewSource from "./views/FullyFeaturedDropzoneView.svelte?raw"; import { sourceParameters } from "./helpers"; export default { title: "Advanced Examples", component: null, }; export const WorkingCSVFileUploadDropzone = { ...sourceParameters(WorkingCSVFileUploadViewSource), render: () => ({ Component: WorkingCSVFileUploadView, }), }; export const FullyFeaturedImagesDropzone = { ...sourceParameters(FullyFeaturedDropzoneViewSource), render: () => ({ Component: FullyFeaturedDropzoneView, }), }; ================================================ FILE: stories/helpers.js ================================================ export const sourceParameters = (source) => ({ parameters: { docs: { source: { code: source, }, }, }, }); ================================================ FILE: stories/views/BasicDropzoneAcceptImagesView.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: stories/views/BasicDropzoneView.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: stories/views/CustomSlotDropzoneView.svelte ================================================

Custom Slot Dropzone

    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: stories/views/DisabledDropzoneView.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: stories/views/FullyFeaturedDropzoneView.svelte ================================================

or

Drag and drop them here

{#if files.accepted.length > 0} {/if} {#each files.accepted as item, index}
{item.name}
{/each}
================================================ FILE: stories/views/NoClickDropzoneView.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: stories/views/NoDragDropzoneView.svelte ================================================
    {#each files.accepted as item}
  1. {item.name}
  2. {/each}
================================================ FILE: stories/views/WorkingCSVFileUploadView.svelte ================================================

Upload CSV file and preview same

{#each files as item}

{item.name}

{/each} {#each fileData as row} {#each row as item} {/each} {/each}
{item}