question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Audio, video or file input

See original GitHub issue

Hi !

I was wondering if you intend to do an audio, video, or a more generic file type. Right now, images are supported as ObjectURL, but I wonder why wouldn’t we want a similar component using react-dropzone as you currently do but that supports other types of files, most likely sending back a File object.

I can understand custom plugins could be covering the use case, but it doesn’t seem you are providing a FileInput primitive within the { Components } from leva/plugin namespace neither, so it’s pretty hard to build it while still matching Leva styles (and even less if you modified the default Leva theme).

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:7

github_iconTop GitHub Comments

3reactions
enzofereycommented, Jun 10, 2021

To speed things up, I hacked together a patch using https://github.com/ds300/patch-package that removes the preview and accepts all type of files returning a File value instead.

I will eventually work on a proper solution to contribute to the repo, but this is an escape patch in case you just need whatever generic file as I do.

The patch
diff --git a/node_modules/leva/dist/leva.cjs.dev.js b/node_modules/leva/dist/leva.cjs.dev.js
index e80df53..40a516c 100644
--- a/node_modules/leva/dist/leva.cjs.dev.js
+++ b/node_modules/leva/dist/leva.cjs.dev.js
@@ -691,16 +691,16 @@ const sanitize$1 = v => {
 
   if (v instanceof File) {
     try {
-      return URL.createObjectURL(v);
+      return v;
     } catch (e) {
       return undefined;
     }
   }
 
-  if (typeof v === 'string' && v.indexOf('blob:') === 0) return v;
-  throw Error(`Invalid image format [undefined | blob | File].`);
+  if (typeof v === 'string') return v;
+  throw Error(`Invalid image format [undefined | string | File].`);
 };
-const schema$1 = (_o, s) => typeof s === 'object' && 'image' in s;
+const schema$1 = (_o, s) => s instanceof File || typeof s === "string";
 const normalize$1 = ({
   image
 }) => {
@@ -719,7 +719,7 @@ var props$1 = /*#__PURE__*/Object.freeze({
 const ImageContainer = vectorPlugin.styled('div', {
   position: 'relative',
   display: 'grid',
-  gridTemplateColumns: '$sizes$rowHeight auto 20px',
+  gridTemplateColumns: 'auto auto',
   columnGap: '$colGap',
   alignItems: 'center'
 });
@@ -838,30 +838,11 @@ function ImageComponent() {
     isDragAccept
   } = reactDropzone.useDropzone({
     maxFiles: 1,
-    accept: 'image/*',
     onDrop
   });
   return React__default['default'].createElement(vectorPlugin.Row, {
     input: true
-  }, React__default['default'].createElement(vectorPlugin.Label, null, label), React__default['default'].createElement(ImageContainer, null, React__default['default'].createElement(ImagePreview, {
-    ref: popinRef,
-    hasImage: !!value,
-    onPointerDown: () => !!value && show(),
-    onPointerUp: hide,
-    style: {
-      backgroundImage: value ? `url(${value})` : 'none'
-    }
-  }), shown && !!value && React__default['default'].createElement(vectorPlugin.Portal, null, React__default['default'].createElement(vectorPlugin.Overlay, {
-    onPointerUp: hide,
-    style: {
-      cursor: 'pointer'
-    }
-  }), React__default['default'].createElement(ImageLargePreview, {
-    ref: wrapperRef,
-    style: {
-      backgroundImage: `url(${value})`
-    }
-  })), React__default['default'].createElement(DropZone, getRootProps({
+  }, React__default['default'].createElement(vectorPlugin.Label, null, label), React__default['default'].createElement(ImageContainer, null, React__default['default'].createElement(DropZone, getRootProps({
     isDragAccept
   }), React__default['default'].createElement("input", getInputProps()), React__default['default'].createElement(Instructions, null, isDragAccept ? 'drop image' : 'click or drop')), React__default['default'].createElement(Remove, {
     onClick: clear,
diff --git a/node_modules/leva/dist/leva.cjs.prod.js b/node_modules/leva/dist/leva.cjs.prod.js
index c9c06ba..eb3033b 100644
--- a/node_modules/leva/dist/leva.cjs.prod.js
+++ b/node_modules/leva/dist/leva.cjs.prod.js
@@ -691,16 +691,16 @@ const sanitize$1 = v => {
 
   if (v instanceof File) {
     try {
-      return URL.createObjectURL(v);
+      return v;
     } catch (e) {
       return undefined;
     }
   }
 
-  if (typeof v === 'string' && v.indexOf('blob:') === 0) return v;
-  throw Error(`Invalid image format [undefined | blob | File].`);
+  if (typeof v === 'string') return v;
+  throw Error(`Invalid image format [undefined | string | File].`);
 };
-const schema$1 = (_o, s) => typeof s === 'object' && 'image' in s;
+const schema$1 = (_o, s) => s instanceof File || typeof s === "string";
 const normalize$1 = ({
   image
 }) => {
@@ -719,7 +719,7 @@ var props$1 = /*#__PURE__*/Object.freeze({
 const ImageContainer = vectorPlugin.styled('div', {
   position: 'relative',
   display: 'grid',
-  gridTemplateColumns: '$sizes$rowHeight auto 20px',
+  gridTemplateColumns: 'auto auto',
   columnGap: '$colGap',
   alignItems: 'center'
 });
@@ -838,30 +838,11 @@ function ImageComponent() {
     isDragAccept
   } = reactDropzone.useDropzone({
     maxFiles: 1,
-    accept: 'image/*',
     onDrop
   });
   return React__default['default'].createElement(vectorPlugin.Row, {
     input: true
-  }, React__default['default'].createElement(vectorPlugin.Label, null, label), React__default['default'].createElement(ImageContainer, null, React__default['default'].createElement(ImagePreview, {
-    ref: popinRef,
-    hasImage: !!value,
-    onPointerDown: () => !!value && show(),
-    onPointerUp: hide,
-    style: {
-      backgroundImage: value ? `url(${value})` : 'none'
-    }
-  }), shown && !!value && React__default['default'].createElement(vectorPlugin.Portal, null, React__default['default'].createElement(vectorPlugin.Overlay, {
-    onPointerUp: hide,
-    style: {
-      cursor: 'pointer'
-    }
-  }), React__default['default'].createElement(ImageLargePreview, {
-    ref: wrapperRef,
-    style: {
-      backgroundImage: `url(${value})`
-    }
-  })), React__default['default'].createElement(DropZone, getRootProps({
+  }, React__default['default'].createElement(vectorPlugin.Label, null, label), React__default['default'].createElement(ImageContainer, null, React__default['default'].createElement(DropZone, getRootProps({
     isDragAccept
   }), React__default['default'].createElement("input", getInputProps()), React__default['default'].createElement(Instructions, null, isDragAccept ? 'drop image' : 'click or drop')), React__default['default'].createElement(Remove, {
     onClick: clear,
diff --git a/node_modules/leva/dist/leva.esm.js b/node_modules/leva/dist/leva.esm.js
index 0cdf36d..1d2f2bf 100644
--- a/node_modules/leva/dist/leva.esm.js
+++ b/node_modules/leva/dist/leva.esm.js
@@ -678,16 +678,16 @@ const sanitize$1 = v => {
 
   if (v instanceof File) {
     try {
-      return URL.createObjectURL(v);
+      return v;
     } catch (e) {
       return undefined;
     }
   }
 
-  if (typeof v === 'string' && v.indexOf('blob:') === 0) return v;
-  throw Error(`Invalid image format [undefined | blob | File].`);
+  if (typeof v === 'string') return v;
+  throw Error(`Invalid image format [undefined | string | File].`);
 };
-const schema$1 = (_o, s) => typeof s === 'object' && 'image' in s;
+const schema$1 = (_o, s) => s instanceof File || typeof s === "string";
 const normalize$1 = ({
   image
 }) => {
@@ -706,7 +706,7 @@ var props$1 = /*#__PURE__*/Object.freeze({
 const ImageContainer = styled('div', {
   position: 'relative',
   display: 'grid',
-  gridTemplateColumns: '$sizes$rowHeight auto 20px',
+  gridTemplateColumns: 'auto auto',
   columnGap: '$colGap',
   alignItems: 'center'
 });
@@ -825,30 +825,11 @@ function ImageComponent() {
     isDragAccept
   } = useDropzone({
     maxFiles: 1,
-    accept: 'image/*',
     onDrop
   });
   return React.createElement(Row, {
     input: true
-  }, React.createElement(Label, null, label), React.createElement(ImageContainer, null, React.createElement(ImagePreview, {
-    ref: popinRef,
-    hasImage: !!value,
-    onPointerDown: () => !!value && show(),
-    onPointerUp: hide,
-    style: {
-      backgroundImage: value ? `url(${value})` : 'none'
-    }
-  }), shown && !!value && React.createElement(Portal, null, React.createElement(Overlay, {
-    onPointerUp: hide,
-    style: {
-      cursor: 'pointer'
-    }
-  }), React.createElement(ImageLargePreview, {
-    ref: wrapperRef,
-    style: {
-      backgroundImage: `url(${value})`
-    }
-  })), React.createElement(DropZone, getRootProps({
+  }, React.createElement(Label, null, label), React.createElement(ImageContainer, null, React.createElement(DropZone, getRootProps({
     isDragAccept
   }), React.createElement("input", getInputProps()), React.createElement(Instructions, null, isDragAccept ? 'drop image' : 'click or drop')), React.createElement(Remove, {
     onClick: clear,
diff --git a/node_modules/leva/src/components/Image/Image.tsx b/node_modules/leva/src/components/Image/Image.tsx
index 75d8840..98bce81 100644
--- a/node_modules/leva/src/components/Image/Image.tsx
+++ b/node_modules/leva/src/components/Image/Image.tsx
@@ -8,7 +8,6 @@ import type { ImageProps } from './image-types'
 
 export function ImageComponent() {
   const { label, value, onUpdate } = useInputContext<ImageProps>()
-  const { popinRef, wrapperRef, shown, show, hide } = usePopin()
 
   const onDrop = useCallback(
     (acceptedFiles) => {
@@ -25,26 +24,13 @@ export function ImageComponent() {
     [onUpdate]
   )
 
-  const { getRootProps, getInputProps, isDragAccept } = useDropzone({ maxFiles: 1, accept: 'image/*', onDrop })
+  const { getRootProps, getInputProps, isDragAccept } = useDropzone({ maxFiles: 1, onDrop })
 
   // TODO fix any in DropZone
   return (
     <Row input>
       <Label>{label}</Label>
       <ImageContainer>
-        <ImagePreview
-          ref={popinRef}
-          hasImage={!!value}
-          onPointerDown={() => !!value && show()}
-          onPointerUp={hide}
-          style={{ backgroundImage: value ? `url(${value})` : 'none' }}
-        />
-        {shown && !!value && (
-          <Portal>
-            <Overlay onPointerUp={hide} style={{ cursor: 'pointer' }} />
-            <ImageLargePreview ref={wrapperRef} style={{ backgroundImage: `url(${value})` }} />
-          </Portal>
-        )}
         <DropZone {...(getRootProps({ isDragAccept }) as any)}>
           <input {...getInputProps()} />
           <Instructions>{isDragAccept ? 'drop image' : 'click or drop'}</Instructions>
diff --git a/node_modules/leva/src/components/Image/image-plugin.ts b/node_modules/leva/src/components/Image/image-plugin.ts
index 887fecd..75d4c18 100644
--- a/node_modules/leva/src/components/Image/image-plugin.ts
+++ b/node_modules/leva/src/components/Image/image-plugin.ts
@@ -1,19 +1,21 @@
 import type { ImageInput } from '../../types'
 
-export const sanitize = (v: any): string | undefined => {
+export const sanitize = (v: any): File | string | undefined => {
   if (v === undefined) return undefined
   if (v instanceof File) {
     try {
-      return URL.createObjectURL(v)
+      return v;
     } catch (e) {
       return undefined
     }
   }
-  if (typeof v === 'string' && v.indexOf('blob:') === 0) return v
-  throw Error(`Invalid image format [undefined | blob | File].`)
+  if(typeof v === "string") {
+    return v;
+  }
+  throw Error(`Invalid image format [undefined | string | File].`)
 }
 
-export const schema = (_o: any, s: any) => typeof s === 'object' && 'image' in s
+export const schema = (_o: any, s: any) => s instanceof File || typeof s === "string";
 
 export const normalize = ({ image }: ImageInput) => {
   return { value: image }
diff --git a/node_modules/leva/src/components/Image/image-types.ts b/node_modules/leva/src/components/Image/image-types.ts
index c9f3b96..a3ef8f2 100644
--- a/node_modules/leva/src/components/Image/image-types.ts
+++ b/node_modules/leva/src/components/Image/image-types.ts
@@ -1,3 +1,3 @@
 import type { LevaInputProps } from '../../types'
 
-export type ImageProps = LevaInputProps<string | undefined>
+export type ImageProps = LevaInputProps<File | string | undefined>
diff --git a/node_modules/leva/src/types/public.ts b/node_modules/leva/src/types/public.ts
index 06a55e1..708df91 100644
--- a/node_modules/leva/src/types/public.ts
+++ b/node_modules/leva/src/types/public.ts
@@ -93,7 +93,7 @@ export type Vector3dInput = MergedInputWithSettings<Vector3d, Vector3dSettings>
 
 export type IntervalInput = { value: [number, number]; min: number; max: number }
 
-export type ImageInput = { image: undefined | string }
+export type ImageInput = { image: undefined | string | File }
 
 type SelectInput = { options: any[] | Record<string, any>; value?: any }
1reaction
enzofereycommented, Nov 26, 2022

Hey @TiagoCavalcante , in case you were asking about updates on my side, I’m using leva on a project that is no longer a priority for me. Feel free to use my patch above or contribute yourself 👍🏻

Read more comments on GitHub >

github_iconTop Results From Across the Web

Video files showing up in file input accept=audio attribute
Yes. If we select all files from the dropdown, it will show all files. But my doubt is why its showing 3gp if...
Read more >
<input type="file"> - HTML: HyperText Markup Language | MDN
A valid MIME type string, with no extensions. The string audio/* meaning "any audio file". The string video/* meaning "any video file". The ......
Read more >
List of all Input Formats - CaptionSync Support Center
List of all Input Formats ;.AIFF, Audio Interchange File Format audio ;.ASF, Windows Media ASF container video ;.AVI, Audio Visual Interleave ...
Read more >
HTML5 - Audio & Video
HTML5 supports <audio> tag which is used to embed sound content in an HTML or XHTML document as follows. ... The current HTML5...
Read more >
Upload & Display Picture, Audio, and Video on the Web Using ...
After reading this article, you'll understand how to set up a form to allow users to upload an image, audio or video using...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found