diff --git a/examples/react/package.json b/examples/react/package.json index 886c5d905..469fdca07 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -11,6 +11,7 @@ "@tailwindcss/vite": "^4.0.9", "@uppy/core": "workspace:*", "@uppy/react": "workspace:*", + "@uppy/screen-capture": "workspace:*", "@uppy/tus": "workspace:*", "@uppy/webcam": "workspace:*", "react": "^19.0.0", diff --git a/examples/react/src/App.tsx b/examples/react/src/App.tsx index a476ee2a8..46da25fd5 100644 --- a/examples/react/src/App.tsx +++ b/examples/react/src/App.tsx @@ -2,156 +2,54 @@ /* eslint-disable react/button-has-type */ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable react/react-in-jsx-scope */ -import { useEffect, useState, useRef } from 'react' +import React, { useState, useRef } from 'react' import { Dropzone, FilesGrid, FilesList, - ProviderIcon, UploadButton, UppyContextProvider, - useWebcam, - useDropzone, - useFileInput, } from '@uppy/react' import Uppy from '@uppy/core' import Tus from '@uppy/tus' import UppyWebcam from '@uppy/webcam' +import UppyScreenCapture from '@uppy/screen-capture' +import Webcam from './Webcam.tsx' +import ScreenCapture from './ScreenCapture.tsx' +import CustomDropzone from './CustomDropzone.tsx' import './app.css' import '@uppy/react/dist/styles.css' -interface WebcamProps { - isOpen: boolean - close: () => void -} - -function Webcam({ isOpen, close }: WebcamProps) { - const { - start, - stop, - getVideoProps, - getSnapshotButtonProps, - getRecordButtonProps, - getStopRecordingButtonProps, - getSubmitButtonProps, - getDiscardButtonProps, - } = useWebcam({ onSubmit: close }) - - useEffect(() => { - if (isOpen) { - start() - } - - return () => { - stop() - } - }, [start, stop, isOpen]) - - return ( -
-
-

Camera

- -
-
- ) -} - -interface CustomDropzoneProps { - openWebcamModal: () => void -} - -function CustomDropzone({ openWebcamModal }: CustomDropzoneProps) { - const { getRootProps, getInputProps } = useDropzone({ - noClick: true, - }) - const { getButtonProps, getInputProps: getFileInputProps } = useFileInput() - - return ( -
- -
-
- - - - -
-
-
- ) -} - function App() { const [uppy] = useState(() => new Uppy() .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/', }) + .use(UppyScreenCapture, { + preferredVideoMimeType: 'video/webm', + displayMediaConstraints: { + video: { + width: 1280, + height: 720, + frameRate: { + ideal: 3, + max: 5, + }, + }, + audio: false, + }, + enableScreenshots: true, + preferredImageMimeType: 'image/webp', + }) .use(UppyWebcam), ) const webcamDialogRef = useRef(null) + const screenCaptureDialogRef = useRef(null) const [isWebcamOpen, setIsWebcamOpen] = useState(false) + const [isScreenCaptureOpen, setIsScreenCaptureOpen] = useState(false) function openWebcamModal() { setIsWebcamOpen(true) @@ -163,6 +61,16 @@ function App() { webcamDialogRef.current?.close() } + function openScreenCaptureModal() { + setIsScreenCaptureOpen(true) + screenCaptureDialogRef.current?.showModal() + } + + function closeScreenCaptureModal() { + setIsScreenCaptureOpen(false) + screenCaptureDialogRef.current?.close() + } + return (
@@ -177,6 +85,16 @@ function App() { closeWebcamModal()} /> + + closeScreenCaptureModal()} + /> + +

With list

@@ -191,7 +109,11 @@ function App() {

With custom dropzone

- openWebcamModal()} /> + openWebcamModal()} + openScreenCaptureModal={() => openScreenCaptureModal()} + /> +
diff --git a/examples/react/src/CustomDropzone.tsx b/examples/react/src/CustomDropzone.tsx new file mode 100644 index 000000000..26777e0ab --- /dev/null +++ b/examples/react/src/CustomDropzone.tsx @@ -0,0 +1,63 @@ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable react/react-in-jsx-scope */ +/* eslint-disable react/button-has-type */ +import { useDropzone, useFileInput, ProviderIcon } from '@uppy/react' + +export interface CustomDropzoneProps { + openWebcamModal: () => void + openScreenCaptureModal: () => void +} + +export function CustomDropzone({ + openWebcamModal, + openScreenCaptureModal, +}: CustomDropzoneProps) { + const { getRootProps, getInputProps } = useDropzone({ noClick: true }) + const { getButtonProps, getInputProps: getFileInputProps } = useFileInput() + + return ( +
+ +
+
+ + + + +
+
+
+ ) +} + +export default CustomDropzone diff --git a/examples/react/src/MediaCapture.tsx b/examples/react/src/MediaCapture.tsx new file mode 100644 index 000000000..58c5f08ce --- /dev/null +++ b/examples/react/src/MediaCapture.tsx @@ -0,0 +1,88 @@ +/* eslint-disable react/jsx-props-no-spreading, react/button-has-type */ +/* eslint-disable react/react-in-jsx-scope */ +import React from 'react' + +type ButtonProps = { + type: 'button' + onClick: () => void + disabled: boolean +} + +export interface MediaCaptureProps { + title: string + close: () => void + getVideoProps: () => Record + getPrimaryActionButtonProps: () => ButtonProps + primaryActionButtonLabel: string + getRecordButtonProps: () => ButtonProps + getStopRecordingButtonProps: () => ButtonProps + getSubmitButtonProps: () => ButtonProps + getDiscardButtonProps: () => ButtonProps +} + +export function MediaCapture({ + title, + close, + getVideoProps, + getPrimaryActionButtonProps, + primaryActionButtonLabel, + getRecordButtonProps, + getStopRecordingButtonProps, + getSubmitButtonProps, + getDiscardButtonProps, +}: MediaCaptureProps) { + return ( +
+
+

{title}

+ +
+ +
+ + + + + +
+
+ ) +} + +export default MediaCapture diff --git a/examples/react/src/ScreenCapture.tsx b/examples/react/src/ScreenCapture.tsx new file mode 100644 index 000000000..64639a99b --- /dev/null +++ b/examples/react/src/ScreenCapture.tsx @@ -0,0 +1,47 @@ +/* eslint-disable react/react-in-jsx-scope */ +import React, { useEffect } from 'react' +import { useScreenCapture } from '@uppy/react' +import MediaCapture from './MediaCapture.tsx' + +export interface ScreenCaptureProps { + isOpen: boolean + close: () => void +} + +export function ScreenCapture({ isOpen, close }: ScreenCaptureProps) { + const { + start, + stop, + getVideoProps, + getScreenshotButtonProps, + getRecordButtonProps, + getStopRecordingButtonProps, + getSubmitButtonProps, + getDiscardButtonProps, + } = useScreenCapture({ onSubmit: close }) + + useEffect(() => { + if (isOpen) { + start() + } + return () => { + stop() + } + }, [start, stop, isOpen]) + + return ( + + ) +} + +export default ScreenCapture diff --git a/examples/react/src/Webcam.tsx b/examples/react/src/Webcam.tsx new file mode 100644 index 000000000..c884a7249 --- /dev/null +++ b/examples/react/src/Webcam.tsx @@ -0,0 +1,48 @@ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable react/react-in-jsx-scope */ +import React, { useEffect } from 'react' +import { useWebcam } from '@uppy/react' +import MediaCapture from './MediaCapture.tsx' + +export interface WebcamProps { + isOpen: boolean + close: () => void +} + +export function Webcam({ isOpen, close }: WebcamProps) { + const { + start, + stop, + getVideoProps, + getSnapshotButtonProps, + getRecordButtonProps, + getStopRecordingButtonProps, + getSubmitButtonProps, + getDiscardButtonProps, + } = useWebcam({ onSubmit: close }) + + useEffect(() => { + if (isOpen) { + start() + } + return () => { + stop() + } + }, [start, stop, isOpen]) + + return ( + + ) +} + +export default Webcam diff --git a/examples/react/tsconfig.json b/examples/react/tsconfig.json index 4df86fb6a..49d7c965f 100644 --- a/examples/react/tsconfig.json +++ b/examples/react/tsconfig.json @@ -9,6 +9,7 @@ "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, + "allowImportingTsExtensions": true, "module": "ESNext", "moduleResolution": "Node", "resolveJsonModule": true, diff --git a/examples/sveltekit/package.json b/examples/sveltekit/package.json index 9fa0fb63d..126584ee4 100644 --- a/examples/sveltekit/package.json +++ b/examples/sveltekit/package.json @@ -16,6 +16,7 @@ "@uppy/dashboard": "workspace:*", "@uppy/drag-drop": "workspace:*", "@uppy/progress-bar": "workspace:*", + "@uppy/screen-capture": "workspace:*", "@uppy/svelte": "workspace:*", "@uppy/tus": "workspace:*", "@uppy/webcam": "workspace:*" diff --git a/examples/sveltekit/src/components/CustomDropzone.svelte b/examples/sveltekit/src/components/CustomDropzone.svelte index 89270b959..2b095e0aa 100644 --- a/examples/sveltekit/src/components/CustomDropzone.svelte +++ b/examples/sveltekit/src/components/CustomDropzone.svelte @@ -3,9 +3,10 @@ interface Props { openWebcamModal: () => void + openScreenCaptureModal: () => void } - const { openWebcamModal }: Props = $props() + const { openWebcamModal, openScreenCaptureModal }: Props = $props() const { getRootProps, getInputProps } = useDropzone({ noClick: true }) const { getButtonProps, getInputProps: getFileInputProps } = useFileInput() @@ -39,6 +40,16 @@ Webcam + + diff --git a/examples/sveltekit/src/components/MediaCapture.svelte b/examples/sveltekit/src/components/MediaCapture.svelte new file mode 100644 index 000000000..ee78dc462 --- /dev/null +++ b/examples/sveltekit/src/components/MediaCapture.svelte @@ -0,0 +1,73 @@ + + +
+
+

{title}

+ +
+ +
+ + + + + +
+
diff --git a/examples/sveltekit/src/components/ScreenCapture.svelte b/examples/sveltekit/src/components/ScreenCapture.svelte new file mode 100644 index 000000000..200b12af3 --- /dev/null +++ b/examples/sveltekit/src/components/ScreenCapture.svelte @@ -0,0 +1,38 @@ + + + diff --git a/examples/sveltekit/src/components/Webcam.svelte b/examples/sveltekit/src/components/Webcam.svelte index 646724252..67df212c1 100644 --- a/examples/sveltekit/src/components/Webcam.svelte +++ b/examples/sveltekit/src/components/Webcam.svelte @@ -1,4 +1,5 @@ -
-
-

Camera

- -
- -
- - - - - -
-
+ diff --git a/examples/sveltekit/src/routes/+page.svelte b/examples/sveltekit/src/routes/+page.svelte index 73f9c7b31..b8744c1aa 100644 --- a/examples/sveltekit/src/routes/+page.svelte +++ b/examples/sveltekit/src/routes/+page.svelte @@ -2,6 +2,7 @@ import Uppy from '@uppy/core' import Tus from '@uppy/tus' import UppyWebcam from '@uppy/webcam' + import UppyScreenCapture from '@uppy/screen-capture' import { UppyContextProvider, Dropzone, @@ -13,16 +14,21 @@ import CustomDropzone from '../components/CustomDropzone.svelte' import Webcam from '../components/Webcam.svelte' + import ScreenCapture from '../components/ScreenCapture.svelte' const uppy = new Uppy() .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/', }) .use(UppyWebcam) + .use(UppyScreenCapture) let webcamDialogRef: HTMLDialogElement let isWebcamOpen = $state(false) + let screenCaptureDialogRef: HTMLDialogElement + let isScreenCaptureOpen = $state(false) + function openWebcamModal() { isWebcamOpen = true webcamDialogRef?.showModal() @@ -32,6 +38,16 @@ isWebcamOpen = false webcamDialogRef?.close() } + + function openScreenCaptureModal() { + isScreenCaptureOpen = true + screenCaptureDialogRef?.showModal() + } + + function closeScreenCaptureModal() { + isScreenCaptureOpen = false + screenCaptureDialogRef?.close() + } @@ -47,6 +63,13 @@ + + + +

With list

@@ -61,7 +84,7 @@

With custom dropzone

- +
diff --git a/examples/vue3/package.json b/examples/vue3/package.json index 8d4f15043..f48c5ae16 100644 --- a/examples/vue3/package.json +++ b/examples/vue3/package.json @@ -13,6 +13,7 @@ "@uppy/dashboard": "workspace:*", "@uppy/drag-drop": "workspace:*", "@uppy/progress-bar": "workspace:*", + "@uppy/screen-capture": "workspace:*", "@uppy/tus": "workspace:*", "@uppy/vue": "workspace:*", "@uppy/webcam": "workspace:*", diff --git a/examples/vue3/src/App.vue b/examples/vue3/src/App.vue index 0f7f907c9..1f53eedd4 100644 --- a/examples/vue3/src/App.vue +++ b/examples/vue3/src/App.vue @@ -12,21 +12,40 @@ + + + +

With list

- +

With grid

- +

With custom dropzone

- +
@@ -45,10 +64,14 @@ import { } from '@uppy/vue' import CustomDropzone from './Dropzone.vue' import Webcam from './Webcam.vue' +import ScreenCapture from './ScreenCapture.vue' import UppyWebcam from '@uppy/webcam' +import UppyScreenCapture from '@uppy/screen-capture' const webcamDialogRef = ref(null) const isWebcamOpen = ref(false) +const screenCaptureDialogRef = ref(null) +const isScreenCaptureOpen = ref(false) function openWebcamModal() { isWebcamOpen.value = true @@ -60,12 +83,23 @@ function closeWebcamModal() { webcamDialogRef.value?.close() } +function openScreenCaptureModal() { + isScreenCaptureOpen.value = true + screenCaptureDialogRef.value?.showModal() +} + +function closeScreenCaptureModal() { + isScreenCaptureOpen.value = false + screenCaptureDialogRef.value?.close() +} + const uppy = computed(() => new Uppy() .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/', }) - .use(UppyWebcam), + .use(UppyWebcam) + .use(UppyScreenCapture), ) diff --git a/examples/vue3/src/Dropzone.vue b/examples/vue3/src/Dropzone.vue index bc9ab7e0a..f2c460178 100644 --- a/examples/vue3/src/Dropzone.vue +++ b/examples/vue3/src/Dropzone.vue @@ -26,6 +26,16 @@ Webcam + + @@ -35,6 +45,7 @@ import { useDropzone, useFileInput, ProviderIcon } from '@uppy/vue' const props = defineProps<{ openWebcamModal: () => void + openScreenCaptureModal: () => void }>() const { getRootProps, getInputProps } = useDropzone({ diff --git a/examples/vue3/src/MediaCapture.vue b/examples/vue3/src/MediaCapture.vue new file mode 100644 index 000000000..9de828494 --- /dev/null +++ b/examples/vue3/src/MediaCapture.vue @@ -0,0 +1,66 @@ + + + diff --git a/examples/vue3/src/ScreenCapture.vue b/examples/vue3/src/ScreenCapture.vue new file mode 100644 index 000000000..1fe1ab114 --- /dev/null +++ b/examples/vue3/src/ScreenCapture.vue @@ -0,0 +1,46 @@ + + + diff --git a/examples/vue3/src/Webcam.vue b/examples/vue3/src/Webcam.vue index dec3c4e74..dc8db0dfb 100644 --- a/examples/vue3/src/Webcam.vue +++ b/examples/vue3/src/Webcam.vue @@ -1,53 +1,21 @@