@useupup/react 3.3.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/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/AudioUploader-2WITC5YR.js +298 -0
- package/dist/AudioUploader-2WITC5YR.js.map +1 -0
- package/dist/AudioUploader-HWP4U6UJ.cjs +298 -0
- package/dist/AudioUploader-HWP4U6UJ.cjs.map +1 -0
- package/dist/BoxUploader-NEGZOMR4.cjs +172 -0
- package/dist/BoxUploader-NEGZOMR4.cjs.map +1 -0
- package/dist/BoxUploader-VE5LRJ7M.js +172 -0
- package/dist/BoxUploader-VE5LRJ7M.js.map +1 -0
- package/dist/CameraUploader-BJ3AU3VS.cjs +200 -0
- package/dist/CameraUploader-BJ3AU3VS.cjs.map +1 -0
- package/dist/CameraUploader-WF5A6UIS.js +200 -0
- package/dist/CameraUploader-WF5A6UIS.js.map +1 -0
- package/dist/DropboxUploader-INTOYHJP.cjs +174 -0
- package/dist/DropboxUploader-INTOYHJP.cjs.map +1 -0
- package/dist/DropboxUploader-VMUYG3OP.js +174 -0
- package/dist/DropboxUploader-VMUYG3OP.js.map +1 -0
- package/dist/GoogleDriveUploader-CXZUZI6R.cjs +177 -0
- package/dist/GoogleDriveUploader-CXZUZI6R.cjs.map +1 -0
- package/dist/GoogleDriveUploader-YA56X3QR.js +177 -0
- package/dist/GoogleDriveUploader-YA56X3QR.js.map +1 -0
- package/dist/ImageEditorInline-GJ3HCPPZ.cjs +185 -0
- package/dist/ImageEditorInline-GJ3HCPPZ.cjs.map +1 -0
- package/dist/ImageEditorInline-N2NSAODR.js +185 -0
- package/dist/ImageEditorInline-N2NSAODR.js.map +1 -0
- package/dist/ImageEditorModal-CWKMDVPY.js +236 -0
- package/dist/ImageEditorModal-CWKMDVPY.js.map +1 -0
- package/dist/ImageEditorModal-XJKPBTVQ.cjs +236 -0
- package/dist/ImageEditorModal-XJKPBTVQ.cjs.map +1 -0
- package/dist/OneDriveUploader-4RPTY7KT.cjs +175 -0
- package/dist/OneDriveUploader-4RPTY7KT.cjs.map +1 -0
- package/dist/OneDriveUploader-I25Y6AJP.js +175 -0
- package/dist/OneDriveUploader-I25Y6AJP.js.map +1 -0
- package/dist/ScreenCaptureUploader-7K6QPD42.cjs +281 -0
- package/dist/ScreenCaptureUploader-7K6QPD42.cjs.map +1 -0
- package/dist/ScreenCaptureUploader-WKDO4S75.js +281 -0
- package/dist/ScreenCaptureUploader-WKDO4S75.js.map +1 -0
- package/dist/chunk-6TFTTLOD.js +852 -0
- package/dist/chunk-6TFTTLOD.js.map +1 -0
- package/dist/chunk-DZQBTC6T.cjs +39 -0
- package/dist/chunk-DZQBTC6T.cjs.map +1 -0
- package/dist/chunk-GNMEQB32.cjs +195 -0
- package/dist/chunk-GNMEQB32.cjs.map +1 -0
- package/dist/chunk-IG62MHVQ.cjs +276 -0
- package/dist/chunk-IG62MHVQ.cjs.map +1 -0
- package/dist/chunk-L3E5OMY7.cjs +852 -0
- package/dist/chunk-L3E5OMY7.cjs.map +1 -0
- package/dist/chunk-LCMDDPUD.js +276 -0
- package/dist/chunk-LCMDDPUD.js.map +1 -0
- package/dist/chunk-N4XGN66E.cjs +79 -0
- package/dist/chunk-N4XGN66E.cjs.map +1 -0
- package/dist/chunk-N7TWHP5Z.js +195 -0
- package/dist/chunk-N7TWHP5Z.js.map +1 -0
- package/dist/chunk-OXWVO7Z6.cjs +70 -0
- package/dist/chunk-OXWVO7Z6.cjs.map +1 -0
- package/dist/chunk-PLGHK2Y4.js +79 -0
- package/dist/chunk-PLGHK2Y4.js.map +1 -0
- package/dist/chunk-SSLP3MEC.js +39 -0
- package/dist/chunk-SSLP3MEC.js.map +1 -0
- package/dist/chunk-WYMKISZI.js +70 -0
- package/dist/chunk-WYMKISZI.js.map +1 -0
- package/dist/index.cjs +4219 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +240 -0
- package/dist/index.d.ts +240 -0
- package/dist/index.js +4219 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.d.ts +4 -0
- package/dist/tailwind-prefixed.css +2529 -0
- package/package.json +110 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Devino
|
|
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
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @useupup/react
|
|
2
|
+
|
|
3
|
+
React file uploader with drag-and-drop, a progress bar, file previews,
|
|
4
|
+
cloud-drive sources (Google Drive, OneDrive, Dropbox, Box), resumable uploads, an
|
|
5
|
+
image editor, theming, and ICU i18n. This is the canonical
|
|
6
|
+
[upup](https://github.com/DevinoSolutions/upup) UI — every other framework
|
|
7
|
+
package mirrors its DOM.
|
|
8
|
+
|
|
9
|
+
Requires React 19 (`react` and `react-dom` are peer dependencies).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm i @useupup/react
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage (Client Mode)
|
|
18
|
+
|
|
19
|
+
Client Mode uploads directly from the browser to your storage; your server only
|
|
20
|
+
issues short-lived upload credentials at `uploadEndpoint`.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
'use client'
|
|
24
|
+
|
|
25
|
+
import { UpupUploader } from '@useupup/react'
|
|
26
|
+
import '@useupup/react/styles'
|
|
27
|
+
|
|
28
|
+
export default function Uploader() {
|
|
29
|
+
return (
|
|
30
|
+
<UpupUploader
|
|
31
|
+
provider="aws"
|
|
32
|
+
uploadEndpoint="/api/upload-token"
|
|
33
|
+
onFileUploadComplete={(file, key) =>
|
|
34
|
+
console.log('Uploaded', file.name, 'to', key)
|
|
35
|
+
}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`uploadEndpoint` is your own route that returns a presigned upload URL — see the
|
|
42
|
+
quickstart for a ready-made handler, or use [`@useupup/server`](https://www.npmjs.com/package/@useupup/server).
|
|
43
|
+
|
|
44
|
+
The stylesheet is a separate import so projects without Tailwind get the same
|
|
45
|
+
look.
|
|
46
|
+
|
|
47
|
+
## Server Mode
|
|
48
|
+
|
|
49
|
+
For credential isolation and server-proxied cloud drives, add
|
|
50
|
+
[`@useupup/server`](https://www.npmjs.com/package/@useupup/server) and point the
|
|
51
|
+
uploader at it:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<UpupUploader mode="server" serverUrl="/api/upup" provider="aws" />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Also exported
|
|
58
|
+
|
|
59
|
+
`UpupThemeProvider`, the brand source icons (`GoogleDriveIcon`, `OneDriveIcon`,
|
|
60
|
+
`DropboxIcon`, `BoxIcon`, `CameraIcon`, and more), and the `use*` uploader hooks
|
|
61
|
+
(`useUpupUpload`, `useUploaderFiles`, `useUploaderContext`, and more) for building
|
|
62
|
+
custom UIs on the same engine.
|
|
63
|
+
|
|
64
|
+
## Links
|
|
65
|
+
|
|
66
|
+
- [React quickstart](https://useupup.com/docs/quickstarts/react)
|
|
67
|
+
- [Documentation](https://useupup.com/docs/)
|
|
68
|
+
- [Source & monorepo](https://github.com/DevinoSolutions/upup)
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SourceViewContainer
|
|
3
|
+
} from "./chunk-SSLP3MEC.js";
|
|
4
|
+
import {
|
|
5
|
+
useUploaderFiles,
|
|
6
|
+
useUploaderSource,
|
|
7
|
+
useUploaderTheme
|
|
8
|
+
} from "./chunk-LCMDDPUD.js";
|
|
9
|
+
|
|
10
|
+
// src/components/AudioUploader.tsx
|
|
11
|
+
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState } from "react";
|
|
12
|
+
import { cn } from "@useupup/core/internal";
|
|
13
|
+
|
|
14
|
+
// src/components/AudioWaveform.tsx
|
|
15
|
+
import { useEffect, useRef } from "react";
|
|
16
|
+
import { jsx } from "react/jsx-runtime";
|
|
17
|
+
var BAR_COUNT = 28;
|
|
18
|
+
function AudioWaveform({
|
|
19
|
+
stream
|
|
20
|
+
}) {
|
|
21
|
+
const barsRef = useRef([]);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
var _a;
|
|
24
|
+
const AudioCtor = (_a = window.AudioContext) != null ? _a : window.webkitAudioContext;
|
|
25
|
+
if (!AudioCtor) return;
|
|
26
|
+
const ctx = new AudioCtor();
|
|
27
|
+
void ctx.resume();
|
|
28
|
+
const source = ctx.createMediaStreamSource(stream);
|
|
29
|
+
const analyser = ctx.createAnalyser();
|
|
30
|
+
analyser.fftSize = 64;
|
|
31
|
+
source.connect(analyser);
|
|
32
|
+
const data = new Uint8Array(analyser.frequencyBinCount);
|
|
33
|
+
let raf = 0;
|
|
34
|
+
const tick = () => {
|
|
35
|
+
var _a2;
|
|
36
|
+
analyser.getByteFrequencyData(data);
|
|
37
|
+
const bars = barsRef.current;
|
|
38
|
+
for (let i = 0; i < bars.length; i++) {
|
|
39
|
+
const v = (_a2 = data[i % data.length]) != null ? _a2 : 0;
|
|
40
|
+
const el = bars[i];
|
|
41
|
+
if (el) el.style.height = `${6 + v / 255 * 34}px`;
|
|
42
|
+
}
|
|
43
|
+
raf = requestAnimationFrame(tick);
|
|
44
|
+
};
|
|
45
|
+
raf = requestAnimationFrame(tick);
|
|
46
|
+
return () => {
|
|
47
|
+
cancelAnimationFrame(raf);
|
|
48
|
+
source.disconnect();
|
|
49
|
+
void ctx.close();
|
|
50
|
+
};
|
|
51
|
+
}, [stream]);
|
|
52
|
+
return /* @__PURE__ */ jsx(
|
|
53
|
+
"div",
|
|
54
|
+
{
|
|
55
|
+
"data-upup-slot": "audio-waveform",
|
|
56
|
+
"aria-hidden": "true",
|
|
57
|
+
className: "upup-flex upup-h-11 upup-items-center upup-justify-center upup-gap-[3px]",
|
|
58
|
+
children: Array.from({ length: BAR_COUNT }).map((_, i) => /* @__PURE__ */ jsx(
|
|
59
|
+
"span",
|
|
60
|
+
{
|
|
61
|
+
ref: (el) => {
|
|
62
|
+
barsRef.current[i] = el;
|
|
63
|
+
},
|
|
64
|
+
className: "upup-w-[3px] upup-flex-none upup-rounded-full upup-bg-[#38bdf8]",
|
|
65
|
+
style: { height: "6px" }
|
|
66
|
+
},
|
|
67
|
+
i
|
|
68
|
+
))
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/components/AudioUploader.tsx
|
|
74
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
75
|
+
function AudioUploader() {
|
|
76
|
+
const { setFiles } = useUploaderFiles();
|
|
77
|
+
const { setActiveSource } = useUploaderSource();
|
|
78
|
+
const { isDark: dark } = useUploaderTheme();
|
|
79
|
+
const [state, setState] = useState("idle");
|
|
80
|
+
const [duration, setDuration] = useState(0);
|
|
81
|
+
const [audioUrl, setAudioUrl] = useState(null);
|
|
82
|
+
const [error, setError] = useState(null);
|
|
83
|
+
const mediaRecorder = useRef2(null);
|
|
84
|
+
const chunks = useRef2([]);
|
|
85
|
+
const timerRef = useRef2(null);
|
|
86
|
+
const streamRef = useRef2(null);
|
|
87
|
+
useEffect2(() => {
|
|
88
|
+
return () => {
|
|
89
|
+
var _a;
|
|
90
|
+
if (timerRef.current) clearInterval(timerRef.current);
|
|
91
|
+
if (audioUrl) URL.revokeObjectURL(audioUrl);
|
|
92
|
+
(_a = streamRef.current) == null ? void 0 : _a.getTracks().forEach((t) => {
|
|
93
|
+
t.stop();
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
}, [audioUrl]);
|
|
97
|
+
const startRecording = useCallback(async () => {
|
|
98
|
+
try {
|
|
99
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
100
|
+
audio: true
|
|
101
|
+
});
|
|
102
|
+
streamRef.current = stream;
|
|
103
|
+
chunks.current = [];
|
|
104
|
+
const recorder = new MediaRecorder(stream);
|
|
105
|
+
mediaRecorder.current = recorder;
|
|
106
|
+
recorder.ondataavailable = (e) => {
|
|
107
|
+
if (e.data.size > 0) chunks.current.push(e.data);
|
|
108
|
+
};
|
|
109
|
+
recorder.onstop = () => {
|
|
110
|
+
const blob = new Blob(chunks.current, {
|
|
111
|
+
type: recorder.mimeType || "audio/webm"
|
|
112
|
+
});
|
|
113
|
+
setAudioUrl(URL.createObjectURL(blob));
|
|
114
|
+
stream.getTracks().forEach((t) => {
|
|
115
|
+
t.stop();
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
recorder.start();
|
|
119
|
+
setState("recording");
|
|
120
|
+
setDuration(0);
|
|
121
|
+
timerRef.current = setInterval(() => {
|
|
122
|
+
setDuration((d) => d + 1);
|
|
123
|
+
}, 1e3);
|
|
124
|
+
} catch {
|
|
125
|
+
setError(
|
|
126
|
+
"Microphone access denied. Please allow microphone access and try again."
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}, []);
|
|
130
|
+
const stopRecording = useCallback(() => {
|
|
131
|
+
var _a;
|
|
132
|
+
(_a = mediaRecorder.current) == null ? void 0 : _a.stop();
|
|
133
|
+
if (timerRef.current) clearInterval(timerRef.current);
|
|
134
|
+
setState("recorded");
|
|
135
|
+
}, []);
|
|
136
|
+
const discardRecording = useCallback(() => {
|
|
137
|
+
if (audioUrl) URL.revokeObjectURL(audioUrl);
|
|
138
|
+
setAudioUrl(null);
|
|
139
|
+
setDuration(0);
|
|
140
|
+
setState("idle");
|
|
141
|
+
}, [audioUrl]);
|
|
142
|
+
const addRecording = useCallback(() => {
|
|
143
|
+
var _a, _b;
|
|
144
|
+
if (!audioUrl) return;
|
|
145
|
+
const ext = ((_b = (_a = mediaRecorder.current) == null ? void 0 : _a.mimeType) == null ? void 0 : _b.includes("webm")) ? "webm" : "ogg";
|
|
146
|
+
void fetch(audioUrl).then((r) => r.blob()).then((blob) => {
|
|
147
|
+
const file = new File(
|
|
148
|
+
[blob],
|
|
149
|
+
`recording-${Date.now()}.${ext}`,
|
|
150
|
+
{ type: blob.type }
|
|
151
|
+
);
|
|
152
|
+
setFiles([file]);
|
|
153
|
+
setActiveSource(void 0);
|
|
154
|
+
}).catch(() => {
|
|
155
|
+
});
|
|
156
|
+
}, [audioUrl, setFiles, setActiveSource]);
|
|
157
|
+
const formatTime = (s) => {
|
|
158
|
+
const m = Math.floor(s / 60);
|
|
159
|
+
const sec = s % 60;
|
|
160
|
+
return `${m}:${sec.toString().padStart(2, "0")}`;
|
|
161
|
+
};
|
|
162
|
+
if (error) {
|
|
163
|
+
return /* @__PURE__ */ jsx2(SourceViewContainer, { "data-upup-slot": "audio-uploader", children: /* @__PURE__ */ jsx2("div", { className: "upup-flex upup-flex-col upup-items-center upup-gap-3 upup-p-6 upup-text-center", children: /* @__PURE__ */ jsx2(
|
|
164
|
+
"p",
|
|
165
|
+
{
|
|
166
|
+
className: cn("upup-text-sm upup-text-red-500", {
|
|
167
|
+
"upup-text-red-400": dark
|
|
168
|
+
}),
|
|
169
|
+
children: error
|
|
170
|
+
}
|
|
171
|
+
) }) });
|
|
172
|
+
}
|
|
173
|
+
return /* @__PURE__ */ jsx2(SourceViewContainer, { "data-upup-slot": "audio-uploader", children: /* @__PURE__ */ jsxs("div", { className: "upup-flex upup-h-full upup-w-full upup-flex-col upup-items-center upup-justify-center upup-gap-6 upup-p-6", children: [
|
|
174
|
+
/* @__PURE__ */ jsx2(
|
|
175
|
+
"div",
|
|
176
|
+
{
|
|
177
|
+
className: cn(
|
|
178
|
+
"upup-flex upup-h-24 upup-w-24 upup-items-center upup-justify-center upup-rounded-full",
|
|
179
|
+
{
|
|
180
|
+
"upup-bg-red-500/20": state === "recording",
|
|
181
|
+
"upup-bg-[#0ea5e9]/20": state === "idle" || state === "recorded"
|
|
182
|
+
}
|
|
183
|
+
),
|
|
184
|
+
children: /* @__PURE__ */ jsx2(
|
|
185
|
+
"div",
|
|
186
|
+
{
|
|
187
|
+
className: cn(
|
|
188
|
+
"upup-flex upup-h-16 upup-w-16 upup-items-center upup-justify-center upup-rounded-full upup-transition-all",
|
|
189
|
+
{
|
|
190
|
+
"upup-animate-pulse upup-bg-red-500": state === "recording",
|
|
191
|
+
"upup-bg-[#0ea5e9]": state === "idle" || state === "recorded"
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
children: /* @__PURE__ */ jsxs(
|
|
195
|
+
"svg",
|
|
196
|
+
{
|
|
197
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
198
|
+
width: "28",
|
|
199
|
+
height: "28",
|
|
200
|
+
viewBox: "0 0 24 24",
|
|
201
|
+
fill: "none",
|
|
202
|
+
stroke: "white",
|
|
203
|
+
strokeWidth: "2",
|
|
204
|
+
strokeLinecap: "round",
|
|
205
|
+
strokeLinejoin: "round",
|
|
206
|
+
children: [
|
|
207
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
|
|
208
|
+
/* @__PURE__ */ jsx2("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
209
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
state === "recording" && streamRef.current && /* @__PURE__ */ jsx2(AudioWaveform, { stream: streamRef.current }),
|
|
218
|
+
/* @__PURE__ */ jsx2(
|
|
219
|
+
"span",
|
|
220
|
+
{
|
|
221
|
+
className: cn(
|
|
222
|
+
"upup-text-2xl upup-font-mono upup-tabular-nums",
|
|
223
|
+
{
|
|
224
|
+
"upup-text-[#1b1b1b]": !dark,
|
|
225
|
+
"upup-text-white": dark
|
|
226
|
+
}
|
|
227
|
+
),
|
|
228
|
+
children: formatTime(duration)
|
|
229
|
+
}
|
|
230
|
+
),
|
|
231
|
+
state === "recorded" && audioUrl && /* @__PURE__ */ jsx2(
|
|
232
|
+
"audio",
|
|
233
|
+
{
|
|
234
|
+
controls: true,
|
|
235
|
+
src: audioUrl,
|
|
236
|
+
className: "upup-w-full upup-max-w-xs"
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
/* @__PURE__ */ jsxs("div", { className: "upup-flex upup-gap-3", children: [
|
|
240
|
+
state === "idle" && /* @__PURE__ */ jsx2(
|
|
241
|
+
"button",
|
|
242
|
+
{
|
|
243
|
+
type: "button",
|
|
244
|
+
className: cn(
|
|
245
|
+
"upup-rounded-lg upup-bg-[#0ea5e9] upup-px-6 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-[#0284c7]",
|
|
246
|
+
{
|
|
247
|
+
"upup-bg-[#38bdf8] hover:upup-bg-[#0ea5e9] dark:upup-bg-[#38bdf8]": dark
|
|
248
|
+
}
|
|
249
|
+
),
|
|
250
|
+
onClick: () => {
|
|
251
|
+
void startRecording();
|
|
252
|
+
},
|
|
253
|
+
children: "Start Recording"
|
|
254
|
+
}
|
|
255
|
+
),
|
|
256
|
+
state === "recording" && /* @__PURE__ */ jsx2(
|
|
257
|
+
"button",
|
|
258
|
+
{
|
|
259
|
+
type: "button",
|
|
260
|
+
className: "upup-rounded-lg upup-bg-red-500 upup-px-6 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-red-600",
|
|
261
|
+
onClick: stopRecording,
|
|
262
|
+
children: "Stop Recording"
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
state === "recorded" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
266
|
+
/* @__PURE__ */ jsx2(
|
|
267
|
+
"button",
|
|
268
|
+
{
|
|
269
|
+
type: "button",
|
|
270
|
+
className: cn(
|
|
271
|
+
"upup-rounded-lg upup-bg-gray-500 upup-px-4 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-gray-600"
|
|
272
|
+
),
|
|
273
|
+
onClick: discardRecording,
|
|
274
|
+
children: "Discard"
|
|
275
|
+
}
|
|
276
|
+
),
|
|
277
|
+
/* @__PURE__ */ jsx2(
|
|
278
|
+
"button",
|
|
279
|
+
{
|
|
280
|
+
type: "button",
|
|
281
|
+
className: cn(
|
|
282
|
+
"upup-rounded-lg upup-bg-[#0ea5e9] upup-px-4 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-[#0284c7]",
|
|
283
|
+
{
|
|
284
|
+
"upup-bg-[#38bdf8] hover:upup-bg-[#0ea5e9] dark:upup-bg-[#38bdf8]": dark
|
|
285
|
+
}
|
|
286
|
+
),
|
|
287
|
+
onClick: addRecording,
|
|
288
|
+
children: "Add Recording"
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
] })
|
|
292
|
+
] })
|
|
293
|
+
] }) });
|
|
294
|
+
}
|
|
295
|
+
export {
|
|
296
|
+
AudioUploader as default
|
|
297
|
+
};
|
|
298
|
+
//# sourceMappingURL=AudioUploader-2WITC5YR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/AudioUploader.tsx","../src/components/AudioWaveform.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react'\r\nimport {\r\n useUploaderFiles,\r\n useUploaderSource,\r\n useUploaderTheme,\r\n} from '../context/UploaderContext'\r\nimport { cn } from '@useupup/core/internal'\r\nimport SourceViewContainer from './shared/SourceViewContainer'\r\nimport AudioWaveform from './AudioWaveform'\r\n\r\ntype RecordingState = 'idle' | 'recording' | 'recorded'\r\n\r\nexport default function AudioUploader(): React.ReactElement | null {\r\n const { setFiles } = useUploaderFiles()\r\n const { setActiveSource } = useUploaderSource()\r\n const { isDark: dark } = useUploaderTheme()\r\n\r\n const [state, setState] = useState<RecordingState>('idle')\r\n const [duration, setDuration] = useState(0)\r\n const [audioUrl, setAudioUrl] = useState<string | null>(null)\r\n const [error, setError] = useState<string | null>(null)\r\n\r\n const mediaRecorder = useRef<MediaRecorder | null>(null)\r\n const chunks = useRef<Blob[]>([])\r\n const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timerRef.current) clearInterval(timerRef.current)\r\n if (audioUrl) URL.revokeObjectURL(audioUrl)\r\n streamRef.current?.getTracks().forEach(t => {\r\n t.stop()\r\n })\r\n }\r\n }, [audioUrl])\r\n\r\n const startRecording = useCallback(async () => {\r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia({\r\n audio: true,\r\n })\r\n streamRef.current = stream\r\n chunks.current = []\r\n const recorder = new MediaRecorder(stream)\r\n mediaRecorder.current = recorder\r\n\r\n recorder.ondataavailable = e => {\r\n if (e.data.size > 0) chunks.current.push(e.data)\r\n }\r\n\r\n recorder.onstop = () => {\r\n const blob = new Blob(chunks.current, {\r\n type: recorder.mimeType || 'audio/webm',\r\n })\r\n setAudioUrl(URL.createObjectURL(blob))\r\n stream.getTracks().forEach(t => {\r\n t.stop()\r\n })\r\n }\r\n\r\n recorder.start()\r\n setState('recording')\r\n setDuration(0)\r\n timerRef.current = setInterval(() => {\r\n setDuration(d => d + 1)\r\n }, 1000)\r\n } catch {\r\n // upup-catch: getUserMedia rejection (permission denied / no device) is surfaced to the user via setError, not a system fault\r\n setError(\r\n 'Microphone access denied. Please allow microphone access and try again.',\r\n )\r\n }\r\n }, [])\r\n\r\n const stopRecording = useCallback(() => {\r\n mediaRecorder.current?.stop()\r\n if (timerRef.current) clearInterval(timerRef.current)\r\n setState('recorded')\r\n }, [])\r\n\r\n const discardRecording = useCallback(() => {\r\n if (audioUrl) URL.revokeObjectURL(audioUrl)\r\n setAudioUrl(null)\r\n setDuration(0)\r\n setState('idle')\r\n }, [audioUrl])\r\n\r\n const addRecording = useCallback(() => {\r\n if (!audioUrl) return\r\n const ext = mediaRecorder.current?.mimeType?.includes('webm')\r\n ? 'webm'\r\n : 'ogg'\r\n void fetch(audioUrl)\r\n .then(r => r.blob())\r\n .then(blob => {\r\n const file = new File(\r\n [blob],\r\n `recording-${Date.now()}.${ext}`,\r\n { type: blob.type },\r\n )\r\n setFiles([file])\r\n setActiveSource(undefined)\r\n })\r\n .catch(() => {\r\n // upup-catch: replaying an in-memory object URL cannot fail in practice; ignore\r\n })\r\n }, [audioUrl, setFiles, setActiveSource])\r\n\r\n const formatTime = (s: number) => {\r\n const m = Math.floor(s / 60)\r\n const sec = s % 60\r\n return `${m}:${sec.toString().padStart(2, '0')}`\r\n }\r\n\r\n if (error) {\r\n return (\r\n <SourceViewContainer data-upup-slot=\"audio-uploader\">\r\n <div className=\"upup-flex upup-flex-col upup-items-center upup-gap-3 upup-p-6 upup-text-center\">\r\n <p\r\n className={cn('upup-text-sm upup-text-red-500', {\r\n 'upup-text-red-400': dark,\r\n })}\r\n >\r\n {error}\r\n </p>\r\n </div>\r\n </SourceViewContainer>\r\n )\r\n }\r\n\r\n return (\r\n <SourceViewContainer data-upup-slot=\"audio-uploader\">\r\n <div className=\"upup-flex upup-h-full upup-w-full upup-flex-col upup-items-center upup-justify-center upup-gap-6 upup-p-6\">\r\n <div\r\n className={cn(\r\n 'upup-flex upup-h-24 upup-w-24 upup-items-center upup-justify-center upup-rounded-full',\r\n {\r\n 'upup-bg-red-500/20': state === 'recording',\r\n 'upup-bg-[#0ea5e9]/20':\r\n state === 'idle' || state === 'recorded',\r\n },\r\n )}\r\n >\r\n <div\r\n className={cn(\r\n 'upup-flex upup-h-16 upup-w-16 upup-items-center upup-justify-center upup-rounded-full upup-transition-all',\r\n {\r\n 'upup-animate-pulse upup-bg-red-500':\r\n state === 'recording',\r\n 'upup-bg-[#0ea5e9]':\r\n state === 'idle' || state === 'recorded',\r\n },\r\n )}\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"28\"\r\n height=\"28\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"white\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n >\r\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\" />\r\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\r\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\" />\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n {state === 'recording' && streamRef.current && (\r\n <AudioWaveform stream={streamRef.current} />\r\n )}\r\n\r\n <span\r\n className={cn(\r\n 'upup-text-2xl upup-font-mono upup-tabular-nums',\r\n {\r\n 'upup-text-[#1b1b1b]': !dark,\r\n 'upup-text-white': dark,\r\n },\r\n )}\r\n >\r\n {formatTime(duration)}\r\n </span>\r\n\r\n {state === 'recorded' && audioUrl && (\r\n <audio\r\n controls\r\n src={audioUrl}\r\n className=\"upup-w-full upup-max-w-xs\"\r\n />\r\n )}\r\n\r\n <div className=\"upup-flex upup-gap-3\">\r\n {state === 'idle' && (\r\n <button\r\n type=\"button\"\r\n className={cn(\r\n 'upup-rounded-lg upup-bg-[#0ea5e9] upup-px-6 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-[#0284c7]',\r\n {\r\n 'upup-bg-[#38bdf8] hover:upup-bg-[#0ea5e9] dark:upup-bg-[#38bdf8]':\r\n dark,\r\n },\r\n )}\r\n onClick={() => {\r\n void startRecording()\r\n }}\r\n >\r\n Start Recording\r\n </button>\r\n )}\r\n {state === 'recording' && (\r\n <button\r\n type=\"button\"\r\n className=\"upup-rounded-lg upup-bg-red-500 upup-px-6 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-red-600\"\r\n onClick={stopRecording}\r\n >\r\n Stop Recording\r\n </button>\r\n )}\r\n {state === 'recorded' && (\r\n <>\r\n <button\r\n type=\"button\"\r\n className={cn(\r\n 'upup-rounded-lg upup-bg-gray-500 upup-px-4 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-gray-600',\r\n )}\r\n onClick={discardRecording}\r\n >\r\n Discard\r\n </button>\r\n <button\r\n type=\"button\"\r\n className={cn(\r\n 'upup-rounded-lg upup-bg-[#0ea5e9] upup-px-4 upup-py-2.5 upup-text-sm upup-font-medium upup-text-white upup-transition-colors hover:upup-bg-[#0284c7]',\r\n {\r\n 'upup-bg-[#38bdf8] hover:upup-bg-[#0ea5e9] dark:upup-bg-[#38bdf8]':\r\n dark,\r\n },\r\n )}\r\n onClick={addRecording}\r\n >\r\n Add Recording\r\n </button>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </SourceViewContainer>\r\n )\r\n}\r\n","import React, { useEffect, useRef } from 'react'\r\n\r\nconst BAR_COUNT = 28\r\n\r\n/**\r\n * Live recording waveform (states-tour-3 state D): sky bars whose heights track\r\n * the microphone's live frequency data via a Web Audio AnalyserNode.\r\n *\r\n * This is the ONE place JS-driven visuals are sanctioned — the bars render REAL\r\n * audio data, not fx decoration, so (a) they are NOT classed `upup-fx-*` (the\r\n * motion gate must not freeze live data) and (b) the rAF read loop is\r\n * legitimate. Heights are written straight to the DOM (no per-frame React\r\n * render). The AnalyserNode + AudioContext are torn down on unmount/stop.\r\n */\r\nexport default function AudioWaveform({\r\n stream,\r\n}: Readonly<{ stream: MediaStream }>): React.ReactElement {\r\n const barsRef = useRef<Array<HTMLSpanElement | null>>([])\r\n\r\n useEffect(() => {\r\n const AudioCtor =\r\n window.AudioContext ??\r\n (window as unknown as { webkitAudioContext?: typeof AudioContext })\r\n .webkitAudioContext\r\n if (!AudioCtor) return\r\n const ctx = new AudioCtor()\r\n // Autoplay policy may create the context suspended; without resuming,\r\n // getByteFrequencyData returns all-zeros and the bars silently flatline.\r\n void ctx.resume()\r\n const source = ctx.createMediaStreamSource(stream)\r\n const analyser = ctx.createAnalyser()\r\n analyser.fftSize = 64\r\n source.connect(analyser)\r\n const data = new Uint8Array(analyser.frequencyBinCount)\r\n let raf = 0\r\n const tick = () => {\r\n analyser.getByteFrequencyData(data)\r\n const bars = barsRef.current\r\n for (let i = 0; i < bars.length; i++) {\r\n const v = data[i % data.length] ?? 0\r\n const el = bars[i]\r\n if (el) el.style.height = `${6 + (v / 255) * 34}px`\r\n }\r\n raf = requestAnimationFrame(tick)\r\n }\r\n raf = requestAnimationFrame(tick)\r\n return () => {\r\n cancelAnimationFrame(raf)\r\n source.disconnect()\r\n void ctx.close()\r\n }\r\n }, [stream])\r\n\r\n return (\r\n <div\r\n data-upup-slot=\"audio-waveform\"\r\n aria-hidden=\"true\"\r\n className=\"upup-flex upup-h-11 upup-items-center upup-justify-center upup-gap-[3px]\"\r\n >\r\n {Array.from({ length: BAR_COUNT }).map((_, i) => (\r\n <span\r\n key={i}\r\n ref={el => {\r\n barsRef.current[i] = el\r\n }}\r\n className=\"upup-w-[3px] upup-flex-none upup-rounded-full upup-bg-[#38bdf8]\"\r\n style={{ height: '6px' }}\r\n />\r\n ))}\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":["useEffect","useRef","_a","jsx","useRef","useEffect"]}
|