upload-motion 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ All notable changes to `upload-motion` are documented here. The project follows [Semantic Versioning](https://semver.org): breaking API changes bump the major version (minor while in 0.x), new features bump the minor version, and fixes bump the patch version.
4
+
5
+ ## 0.1.0 – 2026-10-18
6
+
7
+ Initial release.
8
+
9
+ - Framework-agnostic upload engine (`upload-motion/core`): queue, concurrency, pause/resume, cancel, retry with backoff, validation, rAF-batched snapshots.
10
+ - Transports: `createXhrUploader` (multipart or raw/presigned PUT, real progress) and `createSimulatedUploader` (badged demo transport).
11
+ - React layer: `FileUpload`, `Uploader` with composable parts, `useUploader`, `useDropzone`, and media analysis hooks.
12
+ - 17 motion presets, including the reference-inspired Card Fan, Wave Bar and Hoop Shot.
13
+ - Theming through `appearance` and CSS variables, motion controls, i18n labels, and reduced-motion support.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mohsin Zia
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.
package/README.md CHANGED
@@ -1,3 +1,199 @@
1
- # Temporary Holding Version
1
+ # upload-motion
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **[Live demo and playground →](https://www.m2toolkit.com/npm-packages/upload-motion)**
4
+
5
+ Animated, accessible file uploads for React: 17 art-directed motion presets on top of a real upload engine with byte-level progress, retries, cancellation and validation.
6
+
7
+ - **Real uploads.** XHR transport with true progress, speed and ETA. Supports presigned PUTs to S3/R2/GCS, or bring your own handler.
8
+ - **17 presets.** Wave Bar, Card Fan, Hoop Shot, Segment Ring, Portal, Liquid Fill, Orbit, Image Bloom, Filmstrip, Audio Waveform and more.
9
+ - **A queue that behaves.** Concurrency, manual or auto start, pause/resume, cancel, retry with exponential backoff, single-file mode.
10
+ - **Validation in plain sentences.** For example: “report.pdf is 31 MB; the limit is 25 MB.” Messages are announced to screen readers.
11
+ - **Accessible.** Keyboard operable, ARIA progressbars, live-region announcements, and `prefers-reduced-motion` support.
12
+ - **Lightweight.** React is the only peer dependency, and there are no runtime dependencies. SSR-safe. A framework-agnostic core lives at `upload-motion/core`.
13
+ - **Honest.** Without a handler, files stay “selected”. Simulated transports are flagged and badged, so fake progress never passes as real.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install upload-motion
19
+ ```
20
+
21
+ Import the stylesheet once (app entry or root layout):
22
+
23
+ ```ts
24
+ import 'upload-motion/styles.css';
25
+ ```
26
+
27
+ ## Quick start
28
+
29
+ ```tsx
30
+ 'use client';
31
+
32
+ import { FileUpload, createXhrUploader } from 'upload-motion';
33
+
34
+ const upload = createXhrUploader({ url: '/api/upload' });
35
+
36
+ export function Attachments() {
37
+ return (
38
+ <FileUpload
39
+ preset="wave-bar"
40
+ upload={upload}
41
+ accept="image/*,.pdf"
42
+ maxSize={25 * 1024 * 1024}
43
+ onUploadSuccess={(file, response) => console.log(file.name, response)}
44
+ />
45
+ );
46
+ }
47
+ ```
48
+
49
+ The file arrives as multipart form data in the `file` field. Any non-2xx response becomes an error, and a JSON `{ "error": "…" }` body is shown to the user.
50
+
51
+ > **Server components:** handlers and callbacks are functions, so render `<FileUpload>` from a `'use client'` file. `upload-motion/core` has no client directive and is safe to import on the server.
52
+
53
+ ## Three ways to use it
54
+
55
+ ### 1. Ready-made
56
+
57
+ ```tsx
58
+ <FileUpload
59
+ preset="segment-ring"
60
+ upload={upload}
61
+ maxFiles={8}
62
+ retry={{ attempts: 2 }}
63
+ appearance={{ theme: 'system', accent: '#0ea5e9', radius: 12 }}
64
+ motion={{ speed: 1.2, particles: 0.5 }}
65
+ />
66
+ ```
67
+
68
+ ### 2. Composable
69
+
70
+ ```tsx
71
+ import { Uploader, UploadDropzone, DropzoneContent, UploadFileList, UploadSummary, UploadRejections } from 'upload-motion';
72
+
73
+ <Uploader upload={upload} accept="image/*,video/*">
74
+ <UploadSummary />
75
+ <UploadDropzone>
76
+ <DropzoneContent title="Drop media for your post" />
77
+ </UploadDropzone>
78
+ <UploadRejections />
79
+ <UploadFileList layout="grid" progressVariant="ring" allowPause />
80
+ </Uploader>;
81
+ ```
82
+
83
+ ### 3. Headless
84
+
85
+ ```tsx
86
+ import { useUploader } from 'upload-motion';
87
+
88
+ const { items, totals, dropzone, cancel, retry } = useUploader({ upload, maxFiles: 5 });
89
+
90
+ <div {...dropzone.getRootProps({ focusable: true })}>
91
+ <input {...dropzone.getInputProps()} />
92
+ {dropzone.isDragActive ? 'Drop it!' : 'Drag files or press Enter'}
93
+ </div>;
94
+ ```
95
+
96
+ Or use the engine without React:
97
+
98
+ ```ts
99
+ import { createUploadStore, createXhrUploader } from 'upload-motion/core';
100
+
101
+ const store = createUploadStore({ upload: createXhrUploader({ url: '/api/upload' }) });
102
+ store.subscribe(() => console.log(store.getSnapshot().totals.progress));
103
+ store.addFiles(fileList);
104
+ ```
105
+
106
+ ## Presets
107
+
108
+ | Preset | Character | Best for |
109
+ | --- | --- | --- |
110
+ | `card-fan` | Media cards fan out as files approach; manual Upload button | Media libraries, CMS |
111
+ | `wave-bar` | Compact pill with a travelling wave and pause/cancel/expand | Dashboards, cloud storage |
112
+ | `hoop-shot` | Aim and shoot files into a hoop with real physics; keyboard “Shoot all” | Consumer apps, onboarding |
113
+ | `essential` | Quiet, professional list | Admin panels, forms |
114
+ | `segment-ring` | One dial, one arc per file | Batch uploads |
115
+ | `beam` | Rotating gradient border beam | Dev tools, dark UIs |
116
+ | `magnetic` | Pointer-reactive dot field | Landing pages |
117
+ | `portal` | Files spiral into a rotating portal | Consumer, creative |
118
+ | `liquid` | The zone fills like a vessel | Single large uploads |
119
+ | `stack` | 3D deck with a done tray | Review queues |
120
+ | `orbit` | Files orbit and fall into a core | Multi-file batches |
121
+ | `bloom` | Images bloom from blur to colour | Photo galleries |
122
+ | `page-reveal` | Pages inked by a scan line (real text for text files) | Document management |
123
+ | `filmstrip` | Real frames extracted from videos | Video platforms |
124
+ | `waveform` | Real decoded audio waveform with preview playback | Podcasts, music |
125
+ | `pixel` | Files assemble from 64 flying pixels | Creative tools |
126
+ | `adaptive` | Picks a per-file motion by type (`fileAnimations` to override) | Mixed media |
127
+
128
+ ## Upload handlers
129
+
130
+ ```ts
131
+ type UploadHandler<T> = (ctx: {
132
+ file: File;
133
+ item: UploadItem;
134
+ signal: AbortSignal; // aborted on cancel/pause/remove/unmount
135
+ attempt: number;
136
+ onProgress: (loaded: number, total?: number) => void;
137
+ }) => Promise<T>;
138
+ ```
139
+
140
+ Built-ins:
141
+
142
+ - `createXhrUploader({ url, method, body: 'form-data' | 'raw', headers, formData, fieldName, timeout, withCredentials, parseResponse })`
143
+ - `createSimulatedUploader({ speed, failureRate })` (demos only, always badged as simulated)
144
+
145
+ Presigned upload example:
146
+
147
+ ```ts
148
+ const upload = createXhrUploader({
149
+ method: 'PUT',
150
+ body: 'raw',
151
+ url: async (item) => (await fetch('/api/presign', { method: 'POST', body: JSON.stringify({ name: item.name }) }).then((r) => r.json())).url,
152
+ headers: (item) => ({ 'Content-Type': item.type || 'application/octet-stream' }),
153
+ });
154
+ ```
155
+
156
+ ## Options
157
+
158
+ | Option | Default | Description |
159
+ | --- | --- | --- |
160
+ | `upload` | – | Transport. Without it, files stay `pending`. |
161
+ | `accept` | – | `"image/*,.pdf"` or an array |
162
+ | `maxSize` / `minSize` | – | Bytes per file |
163
+ | `maxFiles` | – | Max files in the queue |
164
+ | `multiple` | `true` | `false` = single file that replaces the current one |
165
+ | `validate` | – | `(file, { items }) => string \| null` |
166
+ | `autoUpload` | `true` (`card-fan`, `hoop-shot`: `false`) | Start on add |
167
+ | `concurrency` | `3` | Parallel uploads |
168
+ | `retry` | `0` | `number` or `{ attempts, delay }` with exponential backoff |
169
+ | `store` | – | External store (uploads survive unmount) |
170
+ | `appearance` | – | `{ theme, accent, accentSecondary, radius, density, shadow, fontFamily, zoneHeight, … }` |
171
+ | `motion` | – | `{ speed, intensity, particles, reducedMotion: 'user' \| 'always' \| 'never' }` |
172
+ | `labels` | – | Override any string (i18n) |
173
+ | `renderFile`, `zoneContent`, `classNames`, `progressVariant`, `layout`, `fileAnimations` | – | Preset customisation |
174
+
175
+ Callbacks: `onFilesAdded`, `onFilesRejected`, `onUploadStart`, `onUploadProgress`, `onUploadSuccess`, `onUploadError`, `onUploadCancel`, `onFileRemoved`, `onQueueComplete`, `onChange`.
176
+
177
+ ## Theming
178
+
179
+ Every token is a CSS custom property on `.um-root` (`--um-accent`, `--um-accent-2`, `--um-surface`, `--um-text`, `--um-radius`, `--um-time`, …), so you can theme with the `appearance` prop or plain CSS. State is exposed as data attributes (`data-um-theme`, `data-um-drag`, `data-status`, `data-kind`).
180
+
181
+ ## Size
182
+
183
+ | What you import | Added to a production app (min + gzip) |
184
+ | --- | --- |
185
+ | `upload-motion/core` only (no React) | ≈ 8 kB |
186
+ | `useUploader` (headless) | ≈ 9 kB |
187
+ | `Uploader` + dropzone + file list | ≈ 15 kB |
188
+ | `FileUpload` with all 17 presets | ≈ 28 kB |
189
+ | `styles.css` (all presets) | ≈ 15 kB |
190
+
191
+ Measured for v0.1.0 in a Vite 8 production build, compared with a React-only baseline.
192
+
193
+ ## Browser support
194
+
195
+ The latest two versions of Chrome, Edge, Firefox and Safari (2024+). Some effects use `color-mix()`, `@property`, container queries and `linear()` easing, and degrade gracefully where those are missing.
196
+
197
+ ## License
198
+
199
+ MIT