Skip to content

Commit ce2efbd

Browse files
committed
upload primitive
1 parent 19d3e11 commit ce2efbd

File tree

12 files changed

+390
-0
lines changed

12 files changed

+390
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ The goal of Solid Primitives is to wrap client and server side functionality to
3434
|[geolocation](https://github.com/solidjs-community/solid-primitives/tree/main/packages/geolocation#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createGeolocation](https://github.com/solidjs-community/solid-primitives/tree/main/packages/geolocation#createGeolocation)<br />[createGeolocationWatcher](https://github.com/solidjs-community/solid-primitives/tree/main/packages/geolocation#createGeolocationWatcher)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/geolocation?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/geolocation)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/geolocation?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/geolocation)|
3535
|[permission](https://github.com/solidjs-community/solid-primitives/tree/main/packages/permission#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createPermission](https://github.com/solidjs-community/solid-primitives/tree/main/packages/permission#createPermission)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/permission?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/permission)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/permission?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/permission)|
3636
|[storage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createStorage)<br />[createCookieStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createCookieStorage)<br />[createAsyncStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createAsyncStorage)<br />[createStorageSignal](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createStorageSignal)<br />[createLocalStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createLocalStorage)<br />[createSessionStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createSessionStorage)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/storage?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/storage)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/storage?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/storage)|
37+
|[upload](https://github.com/solidjs-community/solid-primitives/tree/main/packages/upload#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createUpload](https://github.com/solidjs-community/solid-primitives/tree/main/packages/upload#createUpload)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/upload?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/upload)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/upload?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/upload)|
3738
|[workers](https://github.com/solidjs-community/solid-primitives/tree/main/packages/workers#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createWorker](https://github.com/solidjs-community/solid-primitives/tree/main/packages/workers#createWorker)<br />[createWorkerPool](https://github.com/solidjs-community/solid-primitives/tree/main/packages/workers#createWorkerPool)<br />[createSignaledWorker](https://github.com/solidjs-community/solid-primitives/tree/main/packages/workers#createSignaledWorker)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/workers?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/workers)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/workers?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/workers)|
3839
|[event-listener](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createEventListener](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventListener)<br />[createEventSignal](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventSignal)<br />[createEventListenerMap](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventListenerMap)<br />[createEventStore](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventStore)<br />[createEventListenerBus](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventListenerBus)<br />[WindowEventListener](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#WindowEventListener)<br />[DocumentEventListener](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#DocumentEventListener)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/event-listener?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/event-listener)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/event-listener?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/event-listener)|
3940
|[mutation-observer](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutation-observer#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createMutationObserver](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutation-observer#createMutationObserver)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/mutation-observer?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/mutation-observer)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/mutation-observer?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/mutation-observer)|

packages/upload/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Solid Primitives Working Group
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.

packages/upload/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<p>
2+
<img width="100%" src="https://assets.solidjs.com/banner?type=Primitives&background=tiles&project=Upload" alt="Solid Primitives Upload">
3+
</p>
4+
5+
# @solid-primitives/upload
6+
7+
[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg?style=for-the-badge)](https://lerna.js.org/)
8+
[![size](https://img.shields.io/bundlephobia/minzip/@solid-primitives/upload?style=for-the-badge)](https://bundlephobia.com/package/@solid-primitives/upload)
9+
[![size](https://img.shields.io/npm/v/@solid-primitives/upload?style=for-the-badge)](https://www.npmjs.com/package/@solid-primitives/upload)
10+
[![stage](https://img.shields.io/endpoint?style=for-the-badge&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)
11+
12+
Primitive to make uploading files easy.
13+
14+
## Installation
15+
16+
```bash
17+
npm install @solid-primitives/upload
18+
# or
19+
yarn add @solid-primitives/upload
20+
```
21+
22+
## How to use it
23+
24+
### createUpload
25+
26+
Upload exports getter and setter. Depending on setter's settings, getter will return single file object or array of it.
27+
28+
```ts
29+
const [files, selectFiles] = createUpload();
30+
// single file
31+
selectFiles({ multiple: true }, (files: CustomFile[]) => {
32+
console.log(files);
33+
});
34+
// multiple files
35+
selectFile({}, ({ source, name, size, file }: CustomFile) => {
36+
console.log({ source, name, size, file });
37+
});
38+
```
39+
40+
## Demo
41+
42+
Working example: https://codesandbox.io/s/solid-primitives-upload-ry042x?file=/src/index.tsx
43+
44+
## Changelog
45+
46+
<details>
47+
<summary><b>Expand Changelog</b></summary>
48+
49+
1.0.0
50+
51+
Committed first version of primitive.
52+
53+
</details>

packages/upload/dev/index.html

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
<title>Solid App</title>
9+
<style>
10+
html {
11+
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
12+
}
13+
14+
body {
15+
padding: 0;
16+
margin: 0;
17+
}
18+
19+
a,
20+
button {
21+
cursor: pointer;
22+
}
23+
24+
* {
25+
margin: 0;
26+
}
27+
</style>
28+
</head>
29+
30+
<body>
31+
<noscript>You need to enable JavaScript to run this app.</noscript>
32+
<div id="root"></div>
33+
34+
<script src="./index.tsx" type="module"></script>
35+
</body>
36+
37+
</html>

packages/upload/dev/index.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Accessor, Component, For } from "solid-js";
2+
import { render } from "solid-js/web";
3+
import createUpload, { UploadFile } from "../src";
4+
import "uno.css";
5+
6+
const App: Component = () => {
7+
const [files, selectFiles] = createUpload();
8+
const [file, selectFile] = createUpload();
9+
10+
return (
11+
<div>
12+
<div>
13+
<h5>Upload single file</h5>
14+
<button
15+
onClick={() => {
16+
selectFile({}, ({ source, name, size, file }: UploadFile) => {
17+
console.log({ source, name, size, file });
18+
});
19+
}}
20+
>
21+
Select file
22+
</button>
23+
<p>{(file as Accessor<UploadFile>)()?.name}</p>
24+
</div>
25+
26+
<div>
27+
<h5>Upload multiple files</h5>
28+
<button
29+
onClick={() => {
30+
selectFiles({ multiple: true }, (files: UploadFile[]) => {
31+
console.log(files);
32+
});
33+
}}
34+
>
35+
Select file
36+
</button>
37+
<For each={(files as Accessor<UploadFile[]>)()}>{file => <p>{file.name}</p>}</For>
38+
</div>
39+
</div>
40+
);
41+
};
42+
43+
render(() => <App />, document.getElementById("root"));

packages/upload/dev/tsconfig.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"module": "ESNext",
5+
"moduleResolution": "node",
6+
"allowSyntheticDefaultImports": true,
7+
"esModuleInterop": true,
8+
"jsx": "preserve",
9+
"jsxImportSource": "solid-js",
10+
"types": [
11+
"vite/client"
12+
]
13+
}
14+
}

packages/upload/dev/vite.config.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { defineConfig } from "vite";
2+
import solidPlugin from "vite-plugin-solid";
3+
import Unocss from "unocss/vite";
4+
5+
export default defineConfig({
6+
plugins: [
7+
solidPlugin(),
8+
Unocss({
9+
shortcuts: {
10+
"center-child": "flex justify-center items-center",
11+
caption: "text-sm font-mono leading-tight",
12+
btn: "bg-teal-600 border-1 border-teal-500 hover:bg-teal-500 rounded cursor-pointer center-child select-none text-white font-semibold p-4 py-3 m-2",
13+
"wrapper-h":
14+
"p-6 flex justify-center items-center space-x-4 space-y-0 bg-gray-700 rounded-2xl",
15+
"wrapper-v": "wrapper-h flex-col space-x-0 space-y-4",
16+
node: "p-4 bg-orange-600 rounded m-2"
17+
}
18+
})
19+
],
20+
build: {
21+
target: "esnext",
22+
polyfillDynamicImport: false
23+
}
24+
});

packages/upload/package.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "@solid-primitives/upload",
3+
"version": "1.0.0",
4+
"description": "Primitives for uploading files.",
5+
"author": "Rustam Ashurmatov <rr.ashurmatov.21@gmail.com>",
6+
"license": "MIT",
7+
"homepage": "https://github.com/solidjs-community/solid-primitives/tree/main/packages/upload",
8+
"repository": {
9+
"type": "git",
10+
"url": "git+https://github.com/solidjs-community/solid-primitives.git"
11+
},
12+
"primitive": {
13+
"name": "upload",
14+
"stage": 0,
15+
"list": [
16+
"createUpload"
17+
],
18+
"category": "Browser APIs"
19+
},
20+
"files": [
21+
"dist"
22+
],
23+
"private": false,
24+
"sideEffects": false,
25+
"type": "module",
26+
"main": "./dist/index.cjs",
27+
"module": "./dist/index.js",
28+
"types": "./dist/index.d.ts",
29+
"scripts": {
30+
"start": "vite serve dev --host",
31+
"dev": "yarn start",
32+
"build": "tsup",
33+
"test": "uvu -r solid-register"
34+
},
35+
"keywords": [
36+
"file",
37+
"file upload",
38+
"upload",
39+
"solid",
40+
"primitives"
41+
],
42+
"devDependencies": {
43+
"jsdom": "^19.0.0",
44+
"prettier": "^2.6.1",
45+
"solid-register": "^0.1.8",
46+
"tslib": "^2.3.1",
47+
"tsup": "^5.12.2",
48+
"typescript": "^4.6.3",
49+
"unocss": "0.30.8",
50+
"uvu": "^0.5.3",
51+
"vite": "2.9.1",
52+
"vite-plugin-solid": "2.2.6",
53+
"esbuild-plugin-solid": "^0.4.2"
54+
},
55+
"peerDependencies": {
56+
"solid-js": "^1.3.1"
57+
}
58+
}

packages/upload/src/index.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { Accessor, createSignal } from "solid-js";
2+
3+
/**
4+
* @property `source` - DOMString containing a URL representing the object given in the parameter
5+
*/
6+
export type UploadFile = {
7+
source: string;
8+
name: string;
9+
size: number;
10+
file: File;
11+
};
12+
/**
13+
* @property `accept` - Comma-separated list of one or more file types, or unique file type specifiers
14+
* @link `accept` - https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept
15+
*/
16+
export type UploadSetter = (
17+
{
18+
accept,
19+
multiple
20+
}: {
21+
accept?: string | undefined;
22+
multiple?: boolean | undefined;
23+
},
24+
cb: (files: UploadFile | UploadFile[]) => void
25+
) => void;
26+
27+
function createInputComponent({
28+
multiple = false,
29+
accept = ""
30+
}: {
31+
multiple: boolean;
32+
accept: string;
33+
}) {
34+
const element = document.createElement("input");
35+
element.type = "file";
36+
element.accept = accept;
37+
element.multiple = multiple;
38+
39+
return element;
40+
}
41+
42+
/**
43+
* Primitive to make uploading files easier.
44+
*
45+
* @return files - Uploaded file or files
46+
* @return uploadFiles - Setter function
47+
*
48+
* @example
49+
* ```ts
50+
* const [files, selectFiles] = createUpload();
51+
* // single file
52+
* selectFiles({ multiple: true, accept: "image/*" }, (files: CustomFile[]) => {
53+
* console.log(files);
54+
* });
55+
* // multiple files
56+
* selectFile({}, ({ source, name, size, file }: CustomFile) => {
57+
* console.log({ source, name, size, file });
58+
* });
59+
* ```
60+
*/
61+
const createUpload = (): [Accessor<UploadFile | UploadFile[] | null>, UploadSetter] => {
62+
const [files, setFiles] = createSignal<UploadFile | UploadFile[] | null>(null);
63+
64+
let userCallback: any = () => {};
65+
66+
async function onChange(this: HTMLInputElement, ev: Event) {
67+
const parsedFiles = [];
68+
const target = this;
69+
70+
for (const index in target.files) {
71+
const fileIndex = +index;
72+
if (isNaN(+fileIndex)) {
73+
continue;
74+
}
75+
76+
const file = target.files[fileIndex];
77+
78+
const parsedFile: UploadFile = {
79+
source: URL.createObjectURL(file),
80+
name: file.name,
81+
size: file.size,
82+
file
83+
};
84+
85+
parsedFiles.push(parsedFile);
86+
}
87+
88+
target.removeEventListener("change", onChange);
89+
90+
target.remove();
91+
92+
if (target.multiple) {
93+
setFiles(parsedFiles);
94+
return userCallback(parsedFiles);
95+
}
96+
97+
setFiles(parsedFiles[0]);
98+
return userCallback(parsedFiles[0]);
99+
}
100+
101+
const uploadFile = (
102+
{ accept = "", multiple = false }: { accept?: string; multiple?: boolean },
103+
cb: any
104+
) => {
105+
if (typeof cb === "function") {
106+
userCallback = cb;
107+
}
108+
109+
const inputEL = createInputComponent({ multiple, accept });
110+
111+
inputEL.addEventListener("change", onChange);
112+
inputEL.click();
113+
};
114+
115+
return [files, uploadFile];
116+
};
117+
118+
export default createUpload;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import createUpload from "../src";
2+
import { suite } from "uvu";
3+
import * as assert from "uvu/assert";
4+
5+
const test = suite("createUpload");
6+
7+
test("file upload", () => {
8+
// const [files, selectFiles] = createUpload();
9+
});
10+
11+
test.run();

0 commit comments

Comments
 (0)