react-dropzone#useDropzone TypeScript Examples

The following examples show how to use react-dropzone#useDropzone. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: FileSelector.tsx    From react-csv-importer with MIT License 7 votes vote down vote up
FileSelector: React.FC<{ onSelected: (file: File) => void }> = ({
  onSelected
}) => {
  const onSelectedRef = useRef(onSelected);
  onSelectedRef.current = onSelected;

  const dropHandler = useCallback((acceptedFiles: File[]) => {
    // silently ignore if nothing to do
    if (acceptedFiles.length < 1) {
      return;
    }

    const file = acceptedFiles[0];
    onSelectedRef.current(file);
  }, []);

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop: dropHandler
  });

  const l10n = useLocale('fileStep');

  return (
    <div
      className="CSVImporter_FileSelector"
      data-active={!!isDragActive}
      {...getRootProps()}
    >
      <input {...getInputProps()} />

      {isDragActive ? (
        <span>{l10n.activeDragDropPrompt}</span>
      ) : (
        <span>{l10n.initialDragDropPrompt}</span>
      )}
    </div>
  );
}
Example #2
Source File: ZeroState.tsx    From firebase-tools-ui with Apache License 2.0 6 votes vote down vote up
ZeroState: React.FC<React.PropsWithChildren<unknown>> = () => {
  const { uploadFiles } = useStorageFiles();

  const { getInputProps, getRootProps, isDragActive } = useDropzone({
    onDrop: async (files) => {
      await uploadFiles(files);
    },
  });

  return (
    <div className={styles.zeroStateWrapper} {...getRootProps()}>
      {isDragActive ? (
        <Typography use="body2" theme="secondary">
          Drop files here
        </Typography>
      ) : (
        <div>
          <Typography use="body1" theme="secondary" tag="div">
            No files found
          </Typography>
          <Typography use="body2" theme="secondary" tag="div">
            Drag and drop files to upload
          </Typography>
        </div>
      )}

      <input {...getInputProps()} />
    </div>
  );
}
Example #3
Source File: index.tsx    From ecoleta with MIT License 6 votes vote down vote up
Dropzone: React.FC<Props> = ({ onFileUploaded }) => {

  const [selectedFileUrl, setSelectedFileUrl] = useState('');

  const onDrop = useCallback(acceptedFiles => {
    const file = acceptedFiles[0];

    const fileUrl = URL.createObjectURL(file);
    
    setSelectedFileUrl(fileUrl);
    onFileUploaded(file);
  }, [onFileUploaded]);
  
  const { getRootProps, getInputProps } = useDropzone({
    onDrop,
    accept: 'image/*'
  });

  return (
    <div className="dropzone" {...getRootProps()}>
      <input {...getInputProps()} accept="image/*" />

      { selectedFileUrl 
          ? <img src={selectedFileUrl} alt="Point thumbnail" />
          : (
            <p>
              <FiUpload />
              Imagem do estabelecimento
            </p>
          )
      }

    </div>
  )
}
Example #4
Source File: DropPad.tsx    From slippi-stats with MIT License 6 votes vote down vote up
DropPad: React.FC<Partial<DropzoneOptions>> = (props) => {
  const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject } = useDropzone(props);

  return (
    <Container {...getRootProps({ isDragActive, isDragAccept, isDragReject })}>
      <input {...getInputProps()} />
      <p>Drag SLP files here or click to select</p>
    </Container>
  );
}
Example #5
Source File: Uploader.test.tsx    From twilio-voice-notification-app with Apache License 2.0 6 votes vote down vote up
TestUploader = () => {
  const { getRootProps, getInputProps } = useDropzone();

  return (
    <Uploader
      getInputProps={getInputProps}
      getRootProps={getRootProps}
      selectedFile={null}
    />
  );
}
Example #6
Source File: index.tsx    From nlw-01-omnistack with MIT License 6 votes vote down vote up
Dropzone: React.FC<Props> = ({ onFileUploaded }) => {
  const [selectedFileUrl, setSelectedFileUrl] = useState('');

  const onDrop = useCallback(acceptedFiles => {
    const file = acceptedFiles[0];

    const fileUrl = URL.createObjectURL(file);

    setSelectedFileUrl(fileUrl);
    onFileUploaded(file);
  }, [onFileUploaded])

  const { getRootProps, getInputProps } = useDropzone({
    onDrop,
    accept: 'image/*'
  })

  return (
    <div className="dropzone" {...getRootProps()}>
      <input {...getInputProps()} accept="image/*" />

      { selectedFileUrl
        ? <img src={selectedFileUrl} alt="Point thumbnail" />
        : (
          <p>
            <FiUpload />
            Imagem do estabelecimento
          </p>
        )
      }
    </div>
  )
}
Example #7
Source File: index.tsx    From nlw-ecoleta with MIT License 6 votes vote down vote up
Dropzone: React.FC<Props> = ({ onFileUploaded }) => {
	const [selectedFileUrl, setSelectedFileUrl] = useState('');

	const onDrop = useCallback(acceptedFiles => {
		const file = acceptedFiles[0];
		const fileUrl = URL.createObjectURL(file);

		setSelectedFileUrl(fileUrl);
		onFileUploaded(file);
	}, [onFileUploaded]);

	const { getRootProps, getInputProps } = useDropzone({onDrop, accept: 'image/*'});

	//Renderização do componente
	return (
		<div className="dropzone" {...getRootProps()}>
			<input {...getInputProps()} accept="image/*" />

			{ selectedFileUrl
				? <img src={selectedFileUrl} alt="Point thumbnail" />
				: (
					<p>
						<FiUpload />
            Imagem do estabelecimento
					</p> 
				)
			}
		</div>
	);
}
Example #8
Source File: TorrentListDropzone.tsx    From flood with GNU General Public License v3.0 6 votes vote down vote up
TorrentListDropzone: FC<{children: ReactNode}> = ({children}: {children: ReactNode}) => {
  const {getRootProps, isDragActive} = useDropzone({
    onDrop: handleFileDrop,
    noClick: true,
    noKeyboard: true,
  });

  return (
    <div
      {...getRootProps({onClick: (evt) => evt.preventDefault()})}
      className={`dropzone dropzone--with-overlay torrents ${isDragActive ? 'dropzone--is-dragging' : ''}`}
    >
      {children}
    </div>
  );
}
Example #9
Source File: TableDropzoneWrapper.tsx    From firebase-tools-ui with Apache License 2.0 6 votes vote down vote up
TableDropzoneWrapper: React.FC<
  React.PropsWithChildren<unknown>
> = ({ children }) => {
  const { uploadFiles } = useStorageFiles();

  const { getInputProps, getRootProps, isDragActive } = useDropzone({
    noClick: true,
    noKeyboard: true,
    onDrop: async (files) => {
      await uploadFiles(files);
    },
  });

  return (
    <div {...getRootProps()}>
      {isDragActive ? (
        <div className={styles.dropzoneWrapper}>
          <>{children}</>
          <Typography
            use="body2"
            className={styles.dropWrapper}
            theme="secondary"
          >
            Drop files here
          </Typography>
        </div>
      ) : (
        <>{children}</>
      )}

      <input {...getInputProps()} />
    </div>
  );
}
Example #10
Source File: index.tsx    From NextLevelWeek with MIT License 6 votes vote down vote up
Dropzone: React.FC<Props> = ({ onFileUploaded }) => {
    const [selectedFileUrl, setFileUrl] = useState("");

    const onDrop = useCallback(
        (acceptedFiles) => {
            // console.log(acceptedFiles);
            const file = acceptedFiles[0];
            const fileUrl = URL.createObjectURL(file);

            setFileUrl(fileUrl);
            onFileUploaded(file);
        },
        [onFileUploaded]
    );

    const { getRootProps, getInputProps } = useDropzone({
        onDrop,
        accept: "image/*",
    });

    return (
        <div className="dropzone" {...getRootProps()}>
            <input {...getInputProps()} accept="image/*" />

            {selectedFileUrl ? (
                <img src={selectedFileUrl} alt="Point thumbnail" />
            ) : (
                <p>
                    <FiUpload />
                    Arraste e solte a imagem do estabelecimento aqui
                    <br /> ou clique para selecionar alguma.
                </p>
            )}
        </div>
    );
}
Example #11
Source File: Dropzone.tsx    From devex with GNU General Public License v3.0 6 votes vote down vote up
Dropzone: React.FC<IProps> = ({ dropCb, fromJson }) => {

  const onDrop = useCallback((acceptedFiles: Blob[]) => {
    acceptedFiles.forEach((file: Blob) => {
      const reader = new FileReader()

      reader.onload = () => {
        const parsedFile = JSON.parse(reader.result as string)
        dropCb(fromJson(parsedFile))
      }
      reader.readAsText(file)
    })
  }, [dropCb, fromJson])

  const { getRootProps, getInputProps } = useDropzone({ accept: 'application/json', onDrop })

  return (
    <Container>
      <div {...getRootProps({ className: 'dropzone' })}>
        <input {...getInputProps()} />
        <span className='dropzone-prompt'>Drag and drop or click to load file</span>
      </div>
    </Container>
  )
}
Example #12
Source File: index.tsx    From NLW-1.0 with MIT License 6 votes vote down vote up
Dropzone: React.FC<Props> = ({ onFileUploaded }) => {
  const [selectedFileUrl, setSelectedFileUrl] = useState('');

  const onDrop = useCallback(
    acceptedFiles => {
      const file = acceptedFiles[0];

      const fileUrl = URL.createObjectURL(file);

      setSelectedFileUrl(fileUrl);
      onFileUploaded(file);
    },
    [onFileUploaded],
  );

  const { getRootProps, getInputProps } = useDropzone({
    onDrop,
    accept: 'image/*',
  });

  return (
    <div className="dropzone" {...getRootProps()}>
      <input {...getInputProps()} accept="image/*" />
      {selectedFileUrl ? (
        <img src={selectedFileUrl} alt="" />
      ) : (
        <p>
          <FiUpload />
          Imagem do estabelecimento
        </p>
      )}
    </div>
  );
}
Example #13
Source File: DropDisplay.tsx    From freedeck-configurator with GNU General Public License v3.0 6 votes vote down vote up
DropDisplay = React.forwardRef<
  any,
  {
    onDrop: (acceptedFiles: File[]) => Promise<void> | void;
    previewImage: string;
  }
>(({ onDrop, previewImage }, ref) => {
  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept: [".jpg", ".jpeg", ".png"],
  });
  return (
    <DropWrapper ref={ref}>
      <Drop {...getRootProps({ style: { outline: "none" } })}>
        <input {...getInputProps()} />
        {isDragActive ? (
          <DropHere>Drop Here</DropHere>
        ) : (
          <ImagePreview multiplier={2.5} src={previewImage} />
        )}
      </Drop>
    </DropWrapper>
  );
})
Example #14
Source File: CSVUpload.tsx    From safe-airdrop with MIT License 5 votes vote down vote up
CSVUpload = (props: CSVUploadProps): JSX.Element => {
  const { onChange } = props;
  const onDrop = useCallback(
    (acceptedFiles: File[]) => {
      acceptedFiles.forEach((file) => {
        const reader = new FileReader();
        reader.onload = function (evt) {
          if (!evt.target) {
            return;
          }
          onChange(evt.target.result as string);
        };
        reader.readAsText(file);
      });
    },
    [onChange],
  );

  const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject } = useDropzone({
    maxFiles: 1,
    onDrop,
    accept: {
      text: [".csv"],
    },
  });

  const style = useMemo(
    () => ({
      ...styles.baseStyle,
      ...(isDragActive ? styles.activeStyle : {}),
      ...(isDragAccept ? styles.acceptStyle : {}),
      ...(isDragReject ? styles.rejectStyle : {}),
    }),
    [isDragActive, isDragReject, isDragAccept],
  );

  return (
    <div>
      <div {...getRootProps({ style })}>
        <input {...getInputProps()} />
        <div
          style={{
            display: "inline-flex",
            flexDirection: "row",
            alignItems: "center",
            gap: "8px",
          }}
        >
          <Button size="md" variant="contained" color="primary" component="span">
            Upload CSV
          </Button>
          <Text center size="lg">
            or drop file here
          </Text>
        </div>
      </div>
    </div>
  );
}
Example #15
Source File: index.tsx    From ecoleta with MIT License 5 votes vote down vote up
Dropzone: React.FC<IProps> = ({ onFileUploaded, preview }) => {
  const [selectedFileUrl, setSelectedFileUrl] = useState(() => {
    if (preview) return preview;

    return '';
  });

  useEffect(() => {
    setSelectedFileUrl(preview);
  }, [preview]);

  const onDrop = useCallback(
    acceptedFiles => {
      const file = acceptedFiles[0];

      const fileUrl = URL.createObjectURL(file);

      setSelectedFileUrl(fileUrl);
      onFileUploaded(file);
    },
    [onFileUploaded],
  );

  const { getRootProps, getInputProps } = useDropzone({
    onDrop,
    accept: 'image/*',
  });

  return (
    <Container {...getRootProps()}>
      <input {...getInputProps()} accept="image/*" />

      {selectedFileUrl ? (
        <img src={selectedFileUrl} alt="Point thumbnail" />
      ) : (
        <p>
          <FiUpload />
          Imagem do estabelecimento
        </p>
      )}
    </Container>
  );
}
Example #16
Source File: useCustomDropZone.ts    From twilio-voice-notification-app with Apache License 2.0 5 votes vote down vote up
useCustomDropZone = (
  resetAlert: Function,
  setParsedNumbers: Setter<string[]>,
  setSelectedFile: Setter<File | null>,
  setAlert: Setter<AlertDto>
) => {
  const onDrop = useCallback(
    (acceptedFiles) => {
      setParsedNumbers([]);
      resetAlert();

      acceptedFiles.forEach((file: any) => {
        setSelectedFile(file);
      });
    },
    [resetAlert, setParsedNumbers, setSelectedFile]
  );

  const onDropRejected = useCallback(
    (fileRejections) => {
      const file = fileRejections[0];
      const errorMessage = `${ALERTS.FILE_READ_ERROR} ${
        file.type !== FILE_TYPE ? ALERTS.FILE_REJECT_ERROR : ''
      }`;

      setAlert({
        message: errorMessage,
        type: AlertType.ERROR,
      });
    },
    [setAlert]
  );

  const { getRootProps, getInputProps } = useDropzone({
    onDrop,
    onDropRejected,
    accept: 'text/plain',
    multiple: false,
  });

  return { getRootProps, getInputProps };
}
Example #17
Source File: UploadBox.tsx    From nextclade with MIT License 5 votes vote down vote up
export function UploadBox({ onUpload, children, ...props }: PropsWithChildren<UploaderGenericProps>) {
  const { t } = useTranslation()
  const [errors, setErrors] = useState<string[]>([])

  const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject } = useDropzone({
    onDrop: makeOnDrop({ t, onUpload, setErrors }),
    multiple: false,
  })

  const hasErrors = errors.length > 0

  if (hasErrors) {
    console.warn(`Errors when uploading:\n${errors.map(appendDash).join('\n')}`)
  }

  let state = UploadZoneState.normal
  if (isDragAccept) state = UploadZoneState.accept
  else if (isDragReject) state = UploadZoneState.reject

  const normal = useMemo(
    () => (
      <UploadZoneTextContainer>
        <UploadZoneText>{t('Drag & Drop a file here')}</UploadZoneText>
        <UploadZoneTextOr>{t('or')}</UploadZoneTextOr>
        <UploadZoneButton color="primary">{t('Select a file')}</UploadZoneButton>
      </UploadZoneTextContainer>
    ),
    [t],
  )

  const active = useMemo(
    () => (
      <UploadZoneTextContainer>
        <UploadZoneText>{t('Drop it!')}</UploadZoneText>
      </UploadZoneTextContainer>
    ),
    [t],
  )

  return (
    <UploadZoneWrapper {...props} {...getRootProps()}>
      <UploadZoneInput type="file" {...getInputProps()} />
      <UploadZone state={state}>
        <UploadZoneLeft>{<FileIconsContainer>{children}</FileIconsContainer>}</UploadZoneLeft>
        <UploadZoneRight>{isDragActive ? active : normal}</UploadZoneRight>
      </UploadZone>
    </UploadZoneWrapper>
  )
}
Example #18
Source File: Input.tsx    From rcvr-app with GNU Affero General Public License v3.0 5 votes vote down vote up
FileInput: React.FC<FileInputProps> = ({
  label,
  hint,
  accept,
  ...rest
}) => {
  const [{ onChange: _, value, ...field }, meta, helpers] = useField(rest)
  const showError = Boolean(meta.touched && meta.error)

  const onDrop = React.useCallback(
    (acceptedFiles) => {
      if (acceptedFiles.length > 0) {
        helpers.setValue(acceptedFiles[0])
      } else {
        helpers.setValue(null)
      }
      helpers.setTouched(true)
    },
    [helpers]
  )

  const { getRootProps, getInputProps } = useDropzone({
    onDrop,
    multiple: false,
    accept,
  })

  const resetValue = React.useCallback(
    (event) => {
      event.stopPropagation()
      helpers.setTouched(true)
      helpers.setValue(undefined)
    },
    [helpers]
  )

  return (
    <div>
      <InputContainer>
        <InputElement as="div" css={{ paddingRight: 40 }} {...getRootProps()}>
          <input type="file" {...field} {...rest} {...getInputProps()} />
          <Text
            as="label"
            variant="label"
            htmlFor={field.name}
            className={!!value && 'active'}
          >
            {label}
          </Text>
          <Text>
            {typeof value === 'string' ? value : value?.name}
            &nbsp;
          </Text>
          <OverlayButton onClick={resetValue} type="button" tabIndex={-1}>
            <Icon icon={Trash} size={5} />
          </OverlayButton>
        </InputElement>
        <Underline asError={showError} />
      </InputContainer>
      {showError && <ErrorText variant="fineprint">{meta.error}</ErrorText>}
      {!showError && hint && <HintText variant="fineprint">{hint}</HintText>}
    </div>
  )
}
Example #19
Source File: Start.tsx    From PMTiles with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
function Start(props: {
  setFile: Dispatch<SetStateAction<PMTiles | undefined>>;
}) {
  const onDrop = useCallback((acceptedFiles: File[]) => {
    props.setFile(new PMTiles(new FileSource(acceptedFiles[0])));
  }, []);

  const { acceptedFiles, getRootProps, getInputProps } = useDropzone({
    onDrop,
  });

  let [remoteUrl, setRemoteUrl] = useState<string>("");
  let [selectedExample, setSelectedExample] = useState<number | null>(1);

  const onRemoteUrlChangeHandler = (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    setRemoteUrl(event.target.value);
  };

  const loadExample = (i: number) => {
    return () => {
      props.setFile(new PMTiles(EXAMPLE_FILES[i]));
    };
  };

  const onSubmit = () => {
    // props.setFile(new PMTiles("abcd"));
  };

  return (
    <Container>
      <Header>PMTiles Viewer</Header>
      <Label htmlFor="remoteUrl">Specify a remote URL</Label>
      <Input
        id="remoteUrl"
        placeholder="https://example.com/my_archive.pmtiles"
        onChange={onRemoteUrlChangeHandler}
      ></Input>
      <Button color="gray" onClick={onSubmit}>
        Load
      </Button>
      <Label htmlFor="localFile">Select a local file</Label>
      <Dropzone {...getRootProps()}>
        <input {...getInputProps()} />
        <p>Drag + drop a file here, or click to select</p>
      </Dropzone>
      <Label>Load an example</Label>
      <ExampleList>
        {EXAMPLE_FILES.map((e, i) => (
          <Example key={i} onClick={loadExample(i)}>
            {e}
          </Example>
        ))}
      </ExampleList>
    </Container>
  );
}
Example #20
Source File: StorageUploadField.tsx    From firecms with MIT License 4 votes vote down vote up
function FileDropComponent({
                               storageMeta,
                               disabled,
                               isDraggingOver,
                               onExternalDrop,
                               multipleFilesSupported,
                               droppableProvided,
                               autoFocus,
                               internalValue,
                               property,
                               onClear,
                               metadata,
                               storagePathBuilder,
                               onFileUploadComplete,
                               size,
                               name,
                               helpText
                           }: {
    storageMeta: StorageMeta,
    disabled: boolean,
    isDraggingOver: boolean,
    droppableProvided: any,
    onExternalDrop: (acceptedFiles: File[]) => void,
    multipleFilesSupported: boolean,
    autoFocus: boolean,
    internalValue: StorageFieldItem[],
    property: StringProperty | ArrayProperty<string[]>,
    onClear: (clearedStoragePathOrDownloadUrl: string) => void,
    metadata: any,
    storagePathBuilder: (file: File) => string,
    onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, fileMetadata?: any) => Promise<void>,
    size: PreviewSize,
    name: string,
    helpText: string
}) {

    const snackbarContext = useSnackbarController();

    const classes = useStyles();
    const {
        getRootProps,
        getInputProps,
        isDragActive,
        isDragAccept,
        isDragReject
    } = useDropzone({
        accept: storageMeta.acceptedFiles ? storageMeta.acceptedFiles.map(e => ({ [e]: [] })).reduce((a, b) => ({ ...a, ...b }), {}) : undefined,
        disabled: disabled || isDraggingOver,
        noDragEventsBubbling: true,
        maxSize: storageMeta.maxSize,
        onDrop: onExternalDrop,
        onDropRejected: (fileRejections, event) => {
            for (let fileRejection of fileRejections) {
                for (let error of fileRejection.errors) {
                    snackbarContext.open({
                        type: "error",
                        title: "Error uploading file",
                            message: `File is larger than ${storageMeta.maxSize} bytes`
                        });
                    }
                }
            }
        }
    );

    return (
        <Box
            {...getRootProps()}
            className={clsx(classes.dropZone, {
                [classes.nonActiveDrop]: !isDragActive,
                [classes.activeDrop]: isDragActive,
                [classes.rejectDrop]: isDragReject,
                [classes.acceptDrop]: isDragAccept,
                [classes.disabled]: disabled
            })}
            sx={{
                display: multipleFilesSupported && internalValue.length ? undefined : "flex",
                alignItems: "center"
            }}
        >
            <Box
                {...droppableProvided.droppableProps}
                ref={droppableProvided.innerRef}
                sx={{
                    display: "flex",
                    alignItems: "center",
                    overflow: multipleFilesSupported && internalValue.length ? "auto" : undefined,
                    minHeight: multipleFilesSupported && internalValue.length ? 180 : 250,
                    p: 1,
                    "&::-webkit-scrollbar": {
                        display: "none"
                    }
                }}
            >

                <input
                    autoFocus={autoFocus}
                    {...getInputProps()} />

                {internalValue.map((entry, index) => {
                    let child: any;
                    if (entry.storagePathOrDownloadUrl) {
                        const renderProperty = multipleFilesSupported
                            ? (property as ArrayProperty<string[]>).of as StringProperty
                            : property as StringProperty;
                        child = (
                            <StorageItemPreview
                                name={`storage_preview_${entry.storagePathOrDownloadUrl}`}
                                property={renderProperty}
                                disabled={disabled}
                                value={entry.storagePathOrDownloadUrl}
                                onClear={onClear}
                                size={entry.size}/>
                        );
                    } else if (entry.file) {
                        child = (
                            <StorageUploadProgress
                                entry={entry}
                                metadata={metadata}
                                storagePath={storagePathBuilder(entry.file)}
                                onFileUploadComplete={onFileUploadComplete}
                                size={size}
                            />
                        );
                    }

                    return (
                        <Draggable
                            key={`array_field_${name}_${entry.id}}`}
                            draggableId={`array_field_${name}_${entry.id}}`}
                            index={index}>
                            {(provided, snapshot) => (
                                <Box
                                    ref={provided.innerRef}
                                    {...provided.draggableProps}
                                    {...provided.dragHandleProps}
                                    style={
                                        provided.draggableProps.style
                                    }
                                    sx={{
                                        borderRadius: "4px"
                                    }}
                                >
                                    {child}
                                </Box>
                            )}
                        </Draggable>
                    );
                })
                }

                {droppableProvided.placeholder}


            </Box>

            <Box
                sx={{
                    flexGrow: 1,
                    minHeight: 38,
                    boxSizing: "border-box",
                    m: 2
                }}>
                <Typography align={"center"}
                            variant={"body2"}
                            sx={(theme) => ({
                                color: "#838383",
                                fontWeight: theme.typography.fontWeightMedium
                            })}>
                    {helpText}
                </Typography>
            </Box>

        </Box>
    );
}
Example #21
Source File: File.tsx    From TabMerger with GNU General Public License v3.0 4 votes vote down vote up
export default function File({ setCurrentText, setActiveTab, setImportType }: IFile): JSX.Element {
  // Get uploaded text & move to the next screen for confirmation
  const onDropAccepted = useCallback(
    async ([file]: File[]) => {
      const { name } = file;

      const type = /\.json$/.test(name)
        ? "json"
        : /\.csv$/.test(name)
        ? "csv"
        : /\.txt$/.test(name)
        ? "text"
        : "markdown";

      const text = type === "json" ? JSON.stringify(await new Response(file).json(), null, 4) : await file.text();

      setImportType(type);
      setCurrentText(text);
      setActiveTab("Text");
    },
    [setImportType, setCurrentText, setActiveTab]
  );

  const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, fileRejections } = useDropzone({
    onDropAccepted,
    accept: [
      "application/json",
      "text/plain",
      ".md",
      "text/markdown",
      ".csv",
      "application/vnd.ms-excel",
      "text/csv",
      ""
    ],
    multiple: false,
    maxFiles: 1,
    maxSize: 25e6
  });

  return (
    <>
      <DropZone {...getRootProps()} $isRejected={isDragReject} $isAccepted={isDragAccept}>
        <input {...getInputProps()} />

        <StyledColumn>
          {isDragActive && isDragAccept ? (
            <>
              <FontAwesomeIcon icon="check-circle" size="2x" color="green" />
              <Message $recent>File looks promising... drop it to proceed</Message>
            </>
          ) : isDragActive && isDragReject ? (
            <>
              <FontAwesomeIcon icon="times-circle" size="2x" color="red" />
              <Message $error>{UPLOAD_FILE_ERROR}</Message>
            </>
          ) : (
            <>
              <p>Drop files here to upload</p>
              <h3>OR</h3>
              <UploadIcon icon="upload" size="3x" />

              <br />
              <p>
                Accepted files: <b>.json</b>, <b>.txt</b>, <b>.md</b>, <b>.csv</b>
              </p>

              <p>
                Maximum upload size: <b>25 MB</b>
              </p>
            </>
          )}
        </StyledColumn>
      </DropZone>

      {!isDragActive && fileRejections.length > 0 && <Message $error>{UPLOAD_FILE_ERROR}</Message>}

      <Note>
        <p>Upon successful upload, you will have a chance to confirm!</p>
      </Note>
    </>
  );
}
Example #22
Source File: ListObjects.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
ListObjects = ({ match, history }: IListObjectsProps) => {
  const classes = useStyles();
  const dispatch = useDispatch();

  const rewindEnabled = useSelector(
    (state: AppState) => state.objectBrowser.rewind.rewindEnabled
  );
  const rewindDate = useSelector(
    (state: AppState) => state.objectBrowser.rewind.dateToRewind
  );
  const bucketToRewind = useSelector(
    (state: AppState) => state.objectBrowser.rewind.bucketToRewind
  );
  const versionsMode = useSelector(
    (state: AppState) => state.objectBrowser.versionsMode
  );

  const searchObjects = useSelector(
    (state: AppState) => state.objectBrowser.searchObjects
  );
  const showDeleted = useSelector(
    (state: AppState) => state.objectBrowser.showDeleted
  );
  const detailsOpen = useSelector(
    (state: AppState) => state.objectBrowser.objectDetailsOpen
  );
  const selectedInternalPaths = useSelector(
    (state: AppState) => state.objectBrowser.selectedInternalPaths
  );
  const loading = useSelector(
    (state: AppState) => state.objectBrowser.loadingObjects
  );
  const simplePath = useSelector(
    (state: AppState) => state.objectBrowser.simplePath
  );

  const loadingBucket = useSelector(selBucketDetailsLoading);
  const bucketInfo = useSelector(selBucketDetailsInfo);
  const allowResources = useSelector(
    (state: AppState) => state.console.session.allowResources
  );

  const [records, setRecords] = useState<BucketObjectItem[]>([]);
  const [deleteMultipleOpen, setDeleteMultipleOpen] = useState<boolean>(false);
  const [loadingStartTime, setLoadingStartTime] = useState<number>(0);
  const [loadingMessage, setLoadingMessage] =
    useState<React.ReactNode>(defLoading);
  const [loadingVersioning, setLoadingVersioning] = useState<boolean>(true);
  const [isVersioned, setIsVersioned] = useState<boolean>(false);
  const [loadingLocking, setLoadingLocking] = useState<boolean>(true);
  const [lockingEnabled, setLockingEnabled] = useState<boolean>(false);
  const [rewindSelect, setRewindSelect] = useState<boolean>(false);
  const [selectedObjects, setSelectedObjects] = useState<string[]>([]);
  const [previewOpen, setPreviewOpen] = useState<boolean>(false);
  const [selectedPreview, setSelectedPreview] =
    useState<BucketObjectItem | null>(null);
  const [shareFileModalOpen, setShareFileModalOpen] = useState<boolean>(false);
  const [sortDirection, setSortDirection] = useState<
    "ASC" | "DESC" | undefined
  >("ASC");
  const [currentSortField, setCurrentSortField] = useState<string>("name");
  const [iniLoad, setIniLoad] = useState<boolean>(false);
  const [canShareFile, setCanShareFile] = useState<boolean>(false);
  const [canPreviewFile, setCanPreviewFile] = useState<boolean>(false);
  const [quota, setQuota] = useState<BucketQuota | null>(null);

  const internalPaths = get(match.params, "subpaths", "");
  const bucketName = match.params["bucketName"];

  const fileUpload = useRef<HTMLInputElement>(null);
  const folderUpload = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (folderUpload.current !== null) {
      folderUpload.current.setAttribute("directory", "");
      folderUpload.current.setAttribute("webkitdirectory", "");
    }
  }, [folderUpload]);

  useEffect(() => {
    if (selectedObjects.length === 1) {
      const objectName = selectedObjects[0];

      if (extensionPreview(objectName) !== "none") {
        setCanPreviewFile(true);
      } else {
        setCanPreviewFile(false);
      }

      if (objectName.endsWith("/")) {
        setCanShareFile(false);
      } else {
        setCanShareFile(true);
      }
    } else {
      setCanShareFile(false);
      setCanPreviewFile(false);
    }
  }, [selectedObjects]);

  useEffect(() => {
    if (!quota) {
      api
        .invoke("GET", `/api/v1/buckets/${bucketName}/quota`)
        .then((res: BucketQuota) => {
          let quotaVals = null;

          if (res.quota) {
            quotaVals = res;
          }

          setQuota(quotaVals);
        })
        .catch((err) => {
          console.error("Error Getting Quota Status: ", err.detailedError);
          setQuota(null);
        });
    }
  }, [quota, bucketName]);

  useEffect(() => {
    if (selectedObjects.length > 0) {
      dispatch(setObjectDetailsView(true));
      return;
    }

    if (selectedObjects.length === 0 && selectedInternalPaths === null) {
      dispatch(setObjectDetailsView(false));
    }
  }, [selectedObjects, selectedInternalPaths, dispatch]);

  const displayDeleteObject = hasPermission(bucketName, [
    IAM_SCOPES.S3_DELETE_OBJECT,
  ]);

  const displayListObjects = hasPermission(bucketName, [
    IAM_SCOPES.S3_LIST_BUCKET,
  ]);

  const updateMessage = () => {
    let timeDelta = Date.now() - loadingStartTime;

    if (timeDelta / 1000 >= 6) {
      setLoadingMessage(
        <Fragment>
          <Typography component="h3">
            This operation is taking longer than expected... (
            {Math.ceil(timeDelta / 1000)}s)
          </Typography>
        </Fragment>
      );
    } else if (timeDelta / 1000 >= 3) {
      setLoadingMessage(
        <Typography component="h3">
          This operation is taking longer than expected...
        </Typography>
      );
    }
  };

  useEffect(() => {
    if (!iniLoad) {
      dispatch(setBucketDetailsLoad(true));
      setIniLoad(true);
    }
  }, [iniLoad, dispatch, setIniLoad]);

  useInterval(() => {
    // Your custom logic here
    if (loading) {
      updateMessage();
    }
  }, 1000);

  useEffect(() => {
    if (loadingVersioning) {
      if (displayListObjects) {
        api
          .invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
          .then((res: BucketVersioning) => {
            setIsVersioned(res.is_versioned);
            setLoadingVersioning(false);
          })
          .catch((err: ErrorResponseHandler) => {
            console.error(
              "Error Getting Object Versioning Status: ",
              err.detailedError
            );
            setLoadingVersioning(false);
          });
      } else {
        setLoadingVersioning(false);
        setRecords([]);
      }
    }
  }, [bucketName, loadingVersioning, dispatch, displayListObjects]);

  useEffect(() => {
    if (loadingLocking) {
      if (displayListObjects) {
        api
          .invoke("GET", `/api/v1/buckets/${bucketName}/object-locking`)
          .then((res: BucketObjectLocking) => {
            setLockingEnabled(res.object_locking_enabled);
            setLoadingLocking(false);
          })
          .catch((err: ErrorResponseHandler) => {
            console.error(
              "Error Getting Object Locking Status: ",
              err.detailedError
            );
            setLoadingLocking(false);
          });
      } else {
        setRecords([]);
        setLoadingLocking(false);
      }
    }
  }, [bucketName, loadingLocking, dispatch, displayListObjects]);

  useEffect(() => {
    const decodedIPaths = decodeURLString(internalPaths);

    if (decodedIPaths.endsWith("/") || decodedIPaths === "") {
      dispatch(setObjectDetailsView(false));
      dispatch(setSelectedObjectView(null));
      dispatch(
        setSimplePathHandler(decodedIPaths === "" ? "/" : decodedIPaths)
      );
    } else {
      dispatch(setLoadingObjectInfo(true));
      dispatch(setObjectDetailsView(true));
      dispatch(setLoadingVersions(true));
      dispatch(
        setSelectedObjectView(
          `${decodedIPaths ? `${encodeURLString(decodedIPaths)}` : ``}`
        )
      );
      dispatch(
        setSimplePathHandler(
          `${decodedIPaths.split("/").slice(0, -1).join("/")}/`
        )
      );
    }
  }, [internalPaths, rewindDate, rewindEnabled, dispatch]);

  useEffect(() => {
    dispatch(setSearchObjects(""));
    dispatch(setLoadingObjectsList(true));
    setSelectedObjects([]);
  }, [simplePath, dispatch, setSelectedObjects]);

  useEffect(() => {
    if (loading) {
      if (displayListObjects) {
        let pathPrefix = "";
        if (internalPaths) {
          const decodedPath = decodeURLString(internalPaths);
          pathPrefix = decodedPath.endsWith("/")
            ? decodedPath
            : decodedPath + "/";
        }

        let currentTimestamp = Date.now();
        setLoadingStartTime(currentTimestamp);
        setLoadingMessage(defLoading);

        // We get URL to look into
        let urlTake = `/api/v1/buckets/${bucketName}/objects`;

        // Is rewind enabled?, we use Rewind API
        if (rewindEnabled) {
          if (bucketToRewind !== bucketName) {
            dispatch(resetRewind());
            return;
          }

          if (rewindDate) {
            const rewindParsed = rewindDate.toISOString();

            urlTake = `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}`;
          }
        } else if (showDeleted) {
          // Do we want to display deleted items too?, we use rewind to current time to show everything
          const currDate = new Date();
          const currDateISO = currDate.toISOString();

          urlTake = `/api/v1/buckets/${bucketName}/rewind/${currDateISO}`;
        }

        api
          .invoke(
            "GET",
            `${urlTake}${
              pathPrefix ? `?prefix=${encodeURLString(pathPrefix)}` : ``
            }`
          )
          .then((res: BucketObjectItemsList) => {
            const records: BucketObjectItem[] = res.objects || [];
            const folders: BucketObjectItem[] = [];
            const files: BucketObjectItem[] = [];

            // We separate items between folders or files to display folders at the beginning always.
            records.forEach((record) => {
              // We omit files from the same path
              if (record.name !== decodeURLString(internalPaths)) {
                // this is a folder
                if (record.name.endsWith("/")) {
                  folders.push(record);
                } else {
                  // this is a file
                  files.push(record);
                }
              }
            });

            const recordsInElement = [...folders, ...files];

            if (recordsInElement.length === 0 && pathPrefix !== "") {
              let pathTest = `/api/v1/buckets/${bucketName}/objects${
                internalPaths ? `?prefix=${internalPaths}` : ""
              }`;

              if (rewindEnabled) {
                const rewindParsed = rewindDate.toISOString();

                let pathPrefix = "";
                if (internalPaths) {
                  const decodedPath = decodeURLString(internalPaths);
                  pathPrefix = decodedPath.endsWith("/")
                    ? decodedPath
                    : decodedPath + "/";
                }

                pathTest = `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${
                  pathPrefix ? `?prefix=${encodeURLString(pathPrefix)}` : ``
                }`;
              }

              api
                .invoke("GET", pathTest)
                .then((res: BucketObjectItemsList) => {
                  //It is a file since it has elements in the object, setting file flag and waiting for component mount
                  if (!res.objects) {
                    // It is a folder, we remove loader & set original results list
                    dispatch(setLoadingObjectsList(false));
                    setRecords(recordsInElement);
                  } else {
                    // This code prevents the program from opening a file when a substring of that file is entered as a new folder.
                    // Previously, if there was a file test1.txt and the folder test was created with the same prefix, the program
                    // would open test1.txt instead
                    let found = false;
                    let pathPrefixChopped = pathPrefix.slice(
                      0,
                      pathPrefix.length - 1
                    );
                    for (let i = 0; i < res.objects.length; i++) {
                      if (res.objects[i].name === pathPrefixChopped) {
                        found = true;
                      }
                    }
                    if (
                      (res.objects.length === 1 &&
                        res.objects[0].name.endsWith("/")) ||
                      !found
                    ) {
                      // This is a folder, we set the original results list
                      setRecords(recordsInElement);
                    } else {
                      // This is a file. We change URL & Open file details view.
                      dispatch(setObjectDetailsView(true));
                      dispatch(setSelectedObjectView(internalPaths));

                      // We split the selected object URL & remove the last item to fetch the files list for the parent folder
                      const parentPath = `${decodeURLString(internalPaths)
                        .split("/")
                        .slice(0, -1)
                        .join("/")}/`;

                      api
                        .invoke(
                          "GET",
                          `${urlTake}${
                            pathPrefix
                              ? `?prefix=${encodeURLString(parentPath)}`
                              : ``
                          }`
                        )
                        .then((res: BucketObjectItemsList) => {
                          const records: BucketObjectItem[] = res.objects || [];

                          setRecords(records);
                        })
                        .catch(() => {});
                    }

                    dispatch(setLoadingObjectsList(false));
                  }
                })
                .catch((err: ErrorResponseHandler) => {
                  dispatch(setLoadingObjectsList(false));
                  dispatch(setErrorSnackMessage(err));
                });
            } else {
              setRecords(recordsInElement);
              dispatch(setLoadingObjectsList(false));
            }
          })
          .catch((err: ErrorResponseHandler) => {
            const permitItems = permissionItems(
              bucketName,
              pathPrefix,
              allowResources || []
            );

            if (!permitItems || permitItems.length === 0) {
              dispatch(setErrorSnackMessage(err));
            } else {
              setRecords(permitItems);
            }

            dispatch(setLoadingObjectsList(false));
          });
      } else {
        dispatch(setLoadingObjectsList(false));
      }
    }
  }, [
    loading,
    match,
    dispatch,
    bucketName,
    rewindEnabled,
    rewindDate,
    internalPaths,
    bucketInfo,
    showDeleted,
    displayListObjects,
    bucketToRewind,
    allowResources,
  ]);

  // bucket info
  useEffect(() => {
    if (loadingBucket) {
      api
        .invoke("GET", `/api/v1/buckets/${bucketName}`)
        .then((res: BucketInfo) => {
          dispatch(setBucketDetailsLoad(false));
          dispatch(setBucketInfo(res));
        })
        .catch((err: ErrorResponseHandler) => {
          dispatch(setBucketDetailsLoad(false));
          dispatch(setErrorSnackMessage(err));
        });
    }
  }, [bucketName, loadingBucket, dispatch]);

  const closeDeleteMultipleModalAndRefresh = (refresh: boolean) => {
    setDeleteMultipleOpen(false);

    if (refresh) {
      dispatch(setSnackBarMessage(`Objects deleted successfully.`));
      setSelectedObjects([]);
      dispatch(setLoadingObjectsList(true));
    }
  };

  const handleUploadButton = (e: any) => {
    if (
      e === null ||
      e === undefined ||
      e.target.files === null ||
      e.target.files === undefined
    ) {
      return;
    }
    e.preventDefault();
    var newFiles: File[] = [];

    for (var i = 0; i < e.target.files.length; i++) {
      newFiles.push(e.target.files[i]);
    }
    uploadObject(newFiles, "");

    e.target.value = "";
  };

  const downloadObject = (object: BucketObjectItem) => {
    const identityDownload = encodeURLString(
      `${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}`
    );

    const downloadCall = download(
      bucketName,
      encodeURLString(object.name),
      object.version_id,
      object.size,
      (progress) => {
        dispatch(
          updateProgress({
            instanceID: identityDownload,
            progress: progress,
          })
        );
      },
      () => {
        dispatch(completeObject(identityDownload));
      },
      () => {
        dispatch(failObject(identityDownload));
      },
      () => {
        dispatch(cancelObjectInList(identityDownload));
      }
    );
    const ID = makeid(8);
    storeCallForObjectWithID(ID, downloadCall);
    dispatch(
      setNewObject({
        ID,
        bucketName,
        done: false,
        instanceID: identityDownload,
        percentage: 0,
        prefix: object.name,
        type: "download",
        waitingForFile: true,
        failed: false,
        cancelled: false,
      })
    );

    downloadCall.send();
  };

  const openPath = (idElement: string) => {
    setSelectedObjects([]);

    const newPath = `/buckets/${bucketName}/browse${
      idElement ? `/${encodeURLString(idElement)}` : ``
    }`;
    history.push(newPath);

    dispatch(setObjectDetailsView(true));
    dispatch(setLoadingVersions(true));
    dispatch(
      setSelectedObjectView(
        `${idElement ? `${encodeURLString(idElement)}` : ``}`
      )
    );
  };

  const uploadObject = useCallback(
    (files: File[], folderPath: string): void => {
      let pathPrefix = "";
      if (simplePath) {
        pathPrefix = simplePath.endsWith("/") ? simplePath : simplePath + "/";
      }

      const upload = (
        files: File[],
        bucketName: string,
        path: string,
        folderPath: string
      ) => {
        let uploadPromise = (file: File) => {
          return new Promise((resolve, reject) => {
            let uploadUrl = `api/v1/buckets/${bucketName}/objects/upload`;
            const fileName = file.name;

            const blobFile = new Blob([file], { type: file.type });

            let encodedPath = "";

            const filePath = get(file, "path", "");
            const fileWebkitRelativePath = get(file, "webkitRelativePath", "");

            let relativeFolderPath = folderPath;

            // File was uploaded via drag & drop
            if (filePath !== "") {
              relativeFolderPath = filePath;
            } else if (fileWebkitRelativePath !== "") {
              // File was uploaded using upload button
              relativeFolderPath = fileWebkitRelativePath;
            }

            if (path !== "" || relativeFolderPath !== "") {
              const finalFolderPath = relativeFolderPath
                .split("/")
                .slice(0, -1)
                .join("/");

              const pathClean = path.endsWith("/") ? path.slice(0, -1) : path;

              encodedPath = encodeURLString(
                `${pathClean}${
                  !pathClean.endsWith("/") &&
                  finalFolderPath !== "" &&
                  !finalFolderPath.startsWith("/")
                    ? "/"
                    : ""
                }${finalFolderPath}${
                  !finalFolderPath.endsWith("/") ||
                  (finalFolderPath.trim() === "" && !path.endsWith("/"))
                    ? "/"
                    : ""
                }`
              );
            }

            if (encodedPath !== "") {
              uploadUrl = `${uploadUrl}?prefix=${encodedPath}`;
            }

            const identity = encodeURLString(
              `${bucketName}-${encodedPath}-${new Date().getTime()}-${Math.random()}`
            );

            let xhr = new XMLHttpRequest();
            xhr.open("POST", uploadUrl, true);

            const areMultipleFiles = files.length > 1;
            let errorMessage = `An error occurred while uploading the file${
              areMultipleFiles ? "s" : ""
            }.`;

            const errorMessages: any = {
              413: "Error - File size too large",
            };

            xhr.withCredentials = false;
            xhr.onload = function (event) {
              // resolve promise only when HTTP code is ok
              if (xhr.status >= 200 && xhr.status < 300) {
                dispatch(completeObject(identity));
                resolve({ status: xhr.status });
              } else {
                // reject promise if there was a server error
                if (errorMessages[xhr.status]) {
                  errorMessage = errorMessages[xhr.status];
                } else if (xhr.response) {
                  try {
                    const err = JSON.parse(xhr.response);
                    errorMessage = err.detailedMessage;
                  } catch (e) {
                    errorMessage = "something went wrong";
                  }
                }
                dispatch(failObject(identity));
                reject({ status: xhr.status, message: errorMessage });
              }
            };

            xhr.upload.addEventListener("error", (event) => {
              reject(errorMessage);
              dispatch(failObject(identity));
              return;
            });

            xhr.upload.addEventListener("progress", (event) => {
              const progress = Math.floor((event.loaded * 100) / event.total);

              dispatch(
                updateProgress({
                  instanceID: identity,
                  progress: progress,
                })
              );
            });

            xhr.onerror = () => {
              reject(errorMessage);
              dispatch(failObject(identity));
              return;
            };
            xhr.onloadend = () => {
              if (files.length === 0) {
                dispatch(setLoadingObjectsList(true));
              }
            };
            xhr.onabort = () => {
              dispatch(cancelObjectInList(identity));
            };

            const formData = new FormData();
            if (file.size !== undefined) {
              formData.append(file.size.toString(), blobFile, fileName);
              const ID = makeid(8);
              storeCallForObjectWithID(ID, xhr);
              dispatch(
                setNewObject({
                  ID,
                  bucketName,
                  done: false,
                  instanceID: identity,
                  percentage: 0,
                  prefix: `${decodeURLString(encodedPath)}${fileName}`,
                  type: "upload",
                  waitingForFile: false,
                  failed: false,
                  cancelled: false,
                })
              );

              xhr.send(formData);
            }
          });
        };

        const uploadFilePromises: any = [];
        // open object manager
        dispatch(openList());
        for (let i = 0; i < files.length; i++) {
          const file = files[i];
          uploadFilePromises.push(uploadPromise(file));
        }
        Promise.allSettled(uploadFilePromises).then((results: Array<any>) => {
          const errors = results.filter(
            (result) => result.status === "rejected"
          );
          if (errors.length > 0) {
            const totalFiles = uploadFilePromises.length;
            const successUploadedFiles =
              uploadFilePromises.length - errors.length;
            const err: ErrorResponseHandler = {
              errorMessage: "There were some errors during file upload",
              detailedError: `Uploaded files ${successUploadedFiles}/${totalFiles}`,
            };
            dispatch(setErrorSnackMessage(err));
          }
          // We force objects list reload after all promises were handled
          dispatch(setLoadingObjectsList(true));
          setSelectedObjects([]);
        });
      };

      upload(files, bucketName, pathPrefix, folderPath);
    },
    [bucketName, dispatch, simplePath]
  );

  const onDrop = useCallback(
    (acceptedFiles: any[]) => {
      if (acceptedFiles && acceptedFiles.length > 0) {
        let newFolderPath: string = acceptedFiles[0].path;
        uploadObject(acceptedFiles, newFolderPath);
      }
    },
    [uploadObject]
  );

  const { getRootProps, getInputProps, isDragActive, isDragAccept } =
    useDropzone({
      noClick: true,
      onDrop,
    });

  const dndStyles = useMemo(
    () => ({
      ...baseDnDStyle,
      ...(isDragActive ? activeDnDStyle : {}),
      ...(isDragAccept ? acceptDnDStyle : {}),
    }),
    [isDragActive, isDragAccept]
  );

  const openPreview = () => {
    if (selectedObjects.length === 1) {
      let fileObject: BucketObjectItem | undefined;

      const findFunction = (currValue: BucketObjectItem) =>
        selectedObjects.includes(currValue.name);

      fileObject = filteredRecords.find(findFunction);

      if (fileObject) {
        setSelectedPreview(fileObject);
        setPreviewOpen(true);
      }
    }
  };

  const openShare = () => {
    if (selectedObjects.length === 1) {
      let fileObject: BucketObjectItem | undefined;

      const findFunction = (currValue: BucketObjectItem) =>
        selectedObjects.includes(currValue.name);

      fileObject = filteredRecords.find(findFunction);

      if (fileObject) {
        setSelectedPreview(fileObject);
        setShareFileModalOpen(true);
      }
    }
  };

  const closeShareModal = () => {
    setShareFileModalOpen(false);
    setSelectedPreview(null);
  };

  const filteredRecords = records.filter((b: BucketObjectItem) => {
    if (searchObjects === "") {
      return true;
    } else {
      const objectName = b.name.toLowerCase();
      if (objectName.indexOf(searchObjects.toLowerCase()) >= 0) {
        return true;
      } else {
        return false;
      }
    }
  });

  const rewindCloseModal = () => {
    setRewindSelect(false);
  };

  const closePreviewWindow = () => {
    setPreviewOpen(false);
    setSelectedPreview(null);
  };

  const selectListObjects = (e: React.ChangeEvent<HTMLInputElement>) => {
    const targetD = e.target;
    const value = targetD.value;
    const checked = targetD.checked;

    let elements: string[] = [...selectedObjects]; // We clone the selectedBuckets array

    if (checked) {
      // If the user has checked this field we need to push this to selectedBucketsList
      elements.push(value);
    } else {
      // User has unchecked this field, we need to remove it from the list
      elements = elements.filter((element) => element !== value);
    }
    setSelectedObjects(elements);
    dispatch(setSelectedObjectView(null));

    return elements;
  };

  const sortChange = (sortData: any) => {
    const newSortDirection = get(sortData, "sortDirection", "DESC");
    setCurrentSortField(sortData.sortBy);
    setSortDirection(newSortDirection);
    dispatch(setLoadingObjectsList(true));
  };

  const pageTitle = decodeURLString(internalPaths);
  const currentPath = pageTitle.split("/").filter((i: string) => i !== "");

  const plSelect = filteredRecords;
  const sortASC = plSelect.sort(sortListObjects(currentSortField));

  let payload: BucketObjectItem[] = [];

  if (sortDirection === "ASC") {
    payload = sortASC;
  } else {
    payload = sortASC.reverse();
  }

  const selectAllItems = () => {
    dispatch(setSelectedObjectView(null));

    if (selectedObjects.length === payload.length) {
      setSelectedObjects([]);
      return;
    }

    const elements = payload.map((item) => item.name);
    setSelectedObjects(elements);
  };

  const downloadSelected = () => {
    if (selectedObjects.length !== 0) {
      let itemsToDownload: BucketObjectItem[] = [];

      const filterFunction = (currValue: BucketObjectItem) =>
        selectedObjects.includes(currValue.name);

      itemsToDownload = filteredRecords.filter(filterFunction);

      itemsToDownload.forEach((filteredItem) => {
        downloadObject(filteredItem);
      });
    }
  };
  let uploadPath = [bucketName];
  if (currentPath.length > 0) {
    uploadPath = uploadPath.concat(currentPath);
  }

  const onClosePanel = (forceRefresh: boolean) => {
    dispatch(setSelectedObjectView(null));
    dispatch(setVersionsModeEnabled({ status: false }));
    if (detailsOpen && selectedInternalPaths !== null) {
      // We change URL to be the contained folder

      const decodedPath = decodeURLString(internalPaths);
      const splitURLS = decodedPath.split("/");

      // We remove the last section of the URL as it should be a file
      splitURLS.pop();

      let URLItem = "";

      if (splitURLS && splitURLS.length > 0) {
        URLItem = `${splitURLS.join("/")}/`;
      }

      history.push(`/buckets/${bucketName}/browse/${encodeURLString(URLItem)}`);
    }

    dispatch(setObjectDetailsView(false));
    setSelectedObjects([]);

    if (forceRefresh) {
      dispatch(setLoadingObjectsList(true));
    }
  };

  const setDeletedAction = () => {
    dispatch(setShowDeletedObjects(!showDeleted));
    onClosePanel(true);
  };

  const tableActions: ItemActions[] = [
    {
      type: "view",
      label: "View",
      onClick: openPath,
      sendOnlyId: true,
    },
  ];

  const multiActionButtons = [
    {
      action: downloadSelected,
      label: "Download",
      disabled: selectedObjects.length === 0,
      icon: <DownloadIcon />,
      tooltip: "Download Selected",
    },
    {
      action: openShare,
      label: "Share",
      disabled: selectedObjects.length !== 1 || !canShareFile,
      icon: <ShareIcon />,
      tooltip: "Share Selected File",
    },
    {
      action: openPreview,
      label: "Preview",
      disabled: selectedObjects.length !== 1 || !canPreviewFile,
      icon: <PreviewIcon />,
      tooltip: "Preview Selected File",
    },
    {
      action: () => {
        setDeleteMultipleOpen(true);
      },
      label: "Delete",
      icon: <DeleteIcon />,
      disabled:
        !hasPermission(bucketName, [IAM_SCOPES.S3_DELETE_OBJECT]) ||
        selectedObjects.length === 0 ||
        !displayDeleteObject,
      tooltip: "Delete Selected Files",
    },
  ];

  return (
    <Fragment>
      {shareFileModalOpen && selectedPreview && (
        <ShareFile
          open={shareFileModalOpen}
          closeModalAndRefresh={closeShareModal}
          bucketName={bucketName}
          dataObject={{
            name: selectedPreview.name,
            last_modified: "",
            version_id: selectedPreview.version_id,
          }}
        />
      )}
      {deleteMultipleOpen && (
        <DeleteMultipleObjects
          deleteOpen={deleteMultipleOpen}
          selectedBucket={bucketName}
          selectedObjects={selectedObjects}
          closeDeleteModalAndRefresh={closeDeleteMultipleModalAndRefresh}
          versioning={isVersioned}
        />
      )}
      {rewindSelect && (
        <RewindEnable
          open={rewindSelect}
          closeModalAndRefresh={rewindCloseModal}
          bucketName={bucketName}
        />
      )}
      {previewOpen && (
        <PreviewFileModal
          open={previewOpen}
          bucketName={bucketName}
          object={selectedPreview}
          onClosePreview={closePreviewWindow}
        />
      )}
      <PageLayout variant={"full"}>
        <Grid item xs={12} className={classes.screenTitleContainer}>
          <ScreenTitle
            className={classes.screenTitle}
            icon={
              <span className={classes.listIcon}>
                <BucketsIcon />
              </span>
            }
            title={<span className={classes.titleSpacer}>{bucketName}</span>}
            subTitle={
              <Fragment>
                <Grid item xs={12} className={classes.bucketDetails}>
                  <span className={classes.detailsSpacer}>
                    Created:&nbsp;&nbsp;&nbsp;
                    <strong>{bucketInfo?.creation_date || ""}</strong>
                  </span>
                  <span className={classes.detailsSpacer}>
                    Access:&nbsp;&nbsp;&nbsp;
                    <strong>{bucketInfo?.access || ""}</strong>
                  </span>
                  {bucketInfo && (
                    <Fragment>
                      <span className={classes.detailsSpacer}>
                        {bucketInfo.size && (
                          <Fragment>{niceBytesInt(bucketInfo.size)}</Fragment>
                        )}
                        {bucketInfo.size && quota && (
                          <Fragment> / {niceBytesInt(quota.quota)}</Fragment>
                        )}
                        {bucketInfo.size && bucketInfo.objects ? " - " : ""}
                        {bucketInfo.objects && (
                          <Fragment>
                            {bucketInfo.objects}&nbsp;Object
                            {bucketInfo.objects && bucketInfo.objects !== 1
                              ? "s"
                              : ""}
                          </Fragment>
                        )}
                      </span>
                    </Fragment>
                  )}
                </Grid>
              </Fragment>
            }
            actions={
              <Fragment>
                <div className={classes.actionsSection}>
                  <RBIconButton
                    id={"rewind-objects-list"}
                    tooltip={"Rewind Bucket"}
                    text={"Rewind"}
                    icon={
                      <Badge
                        badgeContent=" "
                        color="secondary"
                        variant="dot"
                        invisible={!rewindEnabled}
                        className={classes.badgeOverlap}
                        sx={{ height: 16 }}
                      >
                        <HistoryIcon
                          style={{
                            minWidth: 16,
                            minHeight: 16,
                            width: 16,
                            height: 16,
                          }}
                        />
                      </Badge>
                    }
                    color="primary"
                    variant={"outlined"}
                    onClick={() => {
                      setRewindSelect(true);
                    }}
                    disabled={
                      !isVersioned ||
                      !hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT])
                    }
                  />
                  <RBIconButton
                    id={"refresh-objects-list"}
                    tooltip={"Reload List"}
                    text={"Refresh"}
                    icon={<RefreshIcon />}
                    color="primary"
                    variant={"outlined"}
                    onClick={() => {
                      if (versionsMode) {
                        dispatch(setLoadingVersions(true));
                      } else {
                        dispatch(setLoadingObjectsList(true));
                      }
                    }}
                    disabled={
                      !hasPermission(bucketName, [IAM_SCOPES.S3_LIST_BUCKET]) ||
                      rewindEnabled
                    }
                  />
                  <input
                    type="file"
                    multiple
                    onChange={handleUploadButton}
                    style={{ display: "none" }}
                    ref={fileUpload}
                  />
                  <input
                    type="file"
                    multiple
                    onChange={handleUploadButton}
                    style={{ display: "none" }}
                    ref={folderUpload}
                  />
                  <UploadFilesButton
                    bucketName={bucketName}
                    uploadPath={uploadPath.join("/")}
                    uploadFileFunction={(closeMenu) => {
                      if (fileUpload && fileUpload.current) {
                        fileUpload.current.click();
                      }
                      closeMenu();
                    }}
                    uploadFolderFunction={(closeMenu) => {
                      if (folderUpload && folderUpload.current) {
                        folderUpload.current.click();
                      }
                      closeMenu();
                    }}
                  />
                </div>
              </Fragment>
            }
          />
        </Grid>
        <div
          id="object-list-wrapper"
          {...getRootProps({ style: { ...dndStyles } })}
        >
          <input {...getInputProps()} />
          <Grid
            item
            xs={12}
            className={classes.tableBlock}
            sx={{ border: "#EAEDEE 1px solid", borderTop: 0 }}
          >
            {versionsMode ? (
              <Fragment>
                {selectedInternalPaths !== null && (
                  <VersionsNavigator
                    internalPaths={selectedInternalPaths}
                    bucketName={bucketName}
                  />
                )}
              </Fragment>
            ) : (
              <SecureComponent
                scopes={[IAM_SCOPES.S3_LIST_BUCKET]}
                resource={bucketName}
                errorProps={{ disabled: true }}
              >
                <Grid item xs={12} className={classes.fullContainer}>
                  <Grid item xs={12} className={classes.breadcrumbsContainer}>
                    <BrowserBreadcrumbs
                      bucketName={bucketName}
                      internalPaths={pageTitle}
                      existingFiles={records || []}
                      additionalOptions={
                        !isVersioned || rewindEnabled ? null : (
                          <div>
                            <CheckboxWrapper
                              name={"deleted_objects"}
                              id={"showDeletedObjects"}
                              value={"deleted_on"}
                              label={"Show deleted objects"}
                              onChange={setDeletedAction}
                              checked={showDeleted}
                              overrideLabelClasses={classes.labelStyle}
                              className={classes.overrideShowDeleted}
                              noTopMargin
                            />
                          </div>
                        )
                      }
                      hidePathButton={false}
                    />
                  </Grid>
                  <TableWrapper
                    itemActions={tableActions}
                    columns={
                      rewindEnabled ? rewindModeColumns : listModeColumns
                    }
                    isLoading={loading}
                    loadingMessage={loadingMessage}
                    entityName="Objects"
                    idField="name"
                    records={payload}
                    customPaperHeight={`${classes.browsePaper} ${
                      detailsOpen ? "actionsPanelOpen" : ""
                    }`}
                    selectedItems={selectedObjects}
                    onSelect={selectListObjects}
                    customEmptyMessage={`This location is empty${
                      !rewindEnabled ? ", please try uploading a new file" : ""
                    }`}
                    sortConfig={{
                      currentSort: currentSortField,
                      currentDirection: sortDirection,
                      triggerSort: sortChange,
                    }}
                    onSelectAll={selectAllItems}
                    rowStyle={({ index }) => {
                      if (payload[index]?.delete_flag) {
                        return "deleted";
                      }

                      return "";
                    }}
                    parentClassName={classes.parentWrapper}
                  />
                </Grid>
              </SecureComponent>
            )}
            <SecureComponent
              scopes={[IAM_SCOPES.S3_LIST_BUCKET]}
              resource={bucketName}
              errorProps={{ disabled: true }}
            >
              <DetailsListPanel
                open={detailsOpen}
                closePanel={() => {
                  onClosePanel(false);
                }}
                className={`${versionsMode ? classes.hideListOnSmall : ""}`}
              >
                {selectedObjects.length > 0 && (
                  <ActionsListSection
                    items={multiActionButtons}
                    title={"Selected Objects:"}
                  />
                )}
                {selectedInternalPaths !== null && (
                  <ObjectDetailPanel
                    internalPaths={selectedInternalPaths}
                    bucketName={bucketName}
                    onClosePanel={onClosePanel}
                    versioning={isVersioned}
                    locking={lockingEnabled}
                  />
                )}
              </DetailsListPanel>
            </SecureComponent>
          </Grid>
        </div>
      </PageLayout>
    </Fragment>
  );
}
Example #23
Source File: dropzone.tsx    From videotranscode.space with Apache License 2.0 4 votes vote down vote up
Dropzone = ({ acceptedFiles }: DropzoneProps) => {
  const [files, setFiles] = useState<Array<FileWithMetadata>>([])

  const [scroll, setScroll] = useState(0)

  const [loading, setLoading] = useState(false)

  const dropzoneRef = React.useRef<HTMLDivElement | null>(null)

  const thumbnailRef = React.useRef<HTMLDivElement | null>(null)

  const { globalReset } = ComponentStore

  useBeforeunload(() => 'Your video will stop processing!')

  useEffect(() => {
    if (globalReset) {
      setFiles([])
    }
  }, [globalReset])

  useEffect(() => {
    setDropzoneRef(dropzoneRef)
    return () => {
      delete FileStore.dropzoneRef
    }
  }, [])

  const translateScroll = (e: WheelEvent) => {
    e.stopPropagation()
    document.body.style.overflowY = 'hidden'
    const maxScroll = thumbnailRef?.current?.scrollHeight || 500
    if (e.deltaY < 0 && scroll > 0) {
      setScroll(s => s - 10)
    } else if (e.deltaY > 0 && scroll < maxScroll) {
      setScroll(s => s + 10)
    }
    if (thumbnailRef && thumbnailRef.current) {
      thumbnailRef.current.scrollTo({ top: scroll, behavior: 'smooth' })
    }
  }

  useEventListener(dropzoneRef, 'wheel', translateScroll)

  const onDrop = useCallback(async acceptedFiles => {
    // Do something with the files

    const newFiles: Array<FileWithMetadata> = await Promise.all(
      acceptedFiles.map(async (file: ElectronFile) => {
        // TODO (rahul) Fix Promise waiting
        if (file.type.match('image')) {
          return {
            file,
            uuid: v4(),
            preview: URL.createObjectURL(file),
            path: file.path || '',
            customType: 'image'
          }
        }
        if (file.type.match('video')) {
          // Generate preview for Video
          try {
            const videoData = await createVideoThumbnail(file)
            return {
              file,
              uuid: v4(),
              preview: videoData.preview,
              path: file.path || '',
              customType: 'video',
              videoMetadata: videoData.videoMetadata
            }
          } catch (err) {
            return {
              file,
              uuid: v4(),
              preview: '',
              path: file.path || '',
              customType: 'video'
            }
          }
        }
        if (file.type.match('audio')) {
          return {
            file,
            uuid: v4(),
            preview: '/images/previews/audioPreview.png',
            customType: 'audio',
            path: file.path || ''
          }
        }

        return { file, preview: '', customType: 'other', path: file.path || '' }
      })
    )
    const transforms: FileTransformType[] = []
    for (const newFile of newFiles) {
      const newTransform: FileTransformType = {
        type: newFile.customType,
        fileObj: newFile,
        state: 'Insert'
      }
      transforms.push(newTransform)
    }
    updateFiles(transforms)

    setFiles(f => f.concat(newFiles))
  }, [])

  const handleDemoVideo = async (e: React.MouseEvent) => {
    setLoading(true)
    e.stopPropagation()
    const demoFile = await fetch('/modfyDemo.webm')
    const blob = await demoFile.blob()
    const file: File = new File([blob], 'modfyDemo.webm', {
      type: 'video/webm'
    })
    const videoData = await createVideoThumbnail(file)
    const fileWithMetadata: FileWithMetadata = {
      file,
      uuid: v4(),
      preview: videoData.preview,
      customType: 'video',
      videoMetadata: videoData.videoMetadata
    }
    const newTransform: FileTransformType = {
      type: fileWithMetadata.customType,
      fileObj: fileWithMetadata,
      state: 'Insert'
    }
    updateFiles([newTransform])
    setFiles(f => f.concat([fileWithMetadata]))
    setLoading(false)
  }

  /**
   * This function gets passed down to DraggableWrapper.
   *
   * It updates the local files state and dispatches the
   * move transform to the files store.
   */
  const moveFiles = (
    oldIndex: number,
    newIndex: number,
    file: FileWithMetadata
  ) => {
    setFiles(items => {
      updateFiles([
        {
          type: file.customType,
          fileObj: file,
          state: 'Move',
          position: oldIndex,
          secondPosition: newIndex
        }
      ])
      return arrayMove(items, oldIndex, newIndex)
    })
  }

  const deleteFile = (index: number, file: FileWithMetadata) => {
    updateFiles([
      {
        type: file.customType,
        fileObj: file,
        state: 'Delete',
        position: index
      }
    ])
    setFiles(items => [...items.slice(0, index), ...items.slice(index + 1)])
  }

  useEffect(() => {
    // This is breaking the previews
    // files.forEach(file => {
    //   if (file.fileWithMetadata.preview)
    //     URL.revokeObjectURL(file.fileWithMetadata.preview)
    // })
  }, [files])

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept: acceptedFiles ?? ['video/*', 'image/*', 'audio/*']
  })

  const keyMap = {
    FILE_DRAWER: ['shift+f']
  }

  const handlers = {
    FILE_DRAWER: (e?: KeyboardEvent) => {
      e?.preventDefault()
      document.getElementById('file-input')?.click()
    }
  }

  if (files.length === 0) {
    return (
      <GlobalHotKeys keyMap={keyMap} handlers={handlers}>
        <div className={styles.previewWrapper}>
          <div
            className={classNames(styles.dropzone, 'dropzone-translate')}
            id="dropzone"
            {...getRootProps()}>
            <div className={styles.scrollableWrapper} ref={dropzoneRef}>
              <input id="file-input" {...getInputProps()} />
              <div className="w-1/3 px-2">
                <img alt="Video file svg" src="/images/upload.svg" />
              </div>
              {isDragActive ? (
                <p className="text-green-500">Drop the files here ...</p>
              ) : (
                <p className="text-green-500">
                  <b>Click</b> or Drag to add files.{' '}
                </p>
              )}
              <button
                type="button"
                onClick={handleDemoVideo}
                disabled={loading}
                className="inline-flex z-20 items-center mt-10 px-4 py-2 border border-transparent shadow-sm text-base font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                {loading ? (
                  <svg
                    className="animate-spin h-5 w-5 mr-3 ..."
                    viewBox="0 0 24 24"
                    fill="currentColor">
                    <path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth={2}
                      d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
                    />
                  </svg>
                ) : null}
                Use a demo file
                {!loading ? (
                  <svg
                    xmlns="http://www.w3.org/2000/svg"
                    fill="none"
                    viewBox="0 0 24 24"
                    className="ml-3 -mr-1 h-5 w-5"
                    stroke="currentColor">
                    <path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth={2}
                      d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z"
                    />
                  </svg>
                ) : null}
              </button>
            </div>
            {/* @ts-ignore Styled JSX */}
            <style jsx>
              {`
                .dropzone {
                  width: ${files.length > 0 ? '90%' : '100%'};
                }
              `}
            </style>
          </div>
        </div>
      </GlobalHotKeys>
    )
  }

  const handleDrop = (e: React.DragEvent<HTMLElement>) => {
    e.preventDefault()

    // Call the drop function to simulate the file being
    // dropped to the dropzone.
    onDrop(
      Array(e.dataTransfer.files.length)
        .fill(0)
        .map((_, idx) => e.dataTransfer.files.item(idx))
    )
  }

  return (
    <GlobalHotKeys keyMap={keyMap} handlers={handlers}>
      <div className={styles.previewWrapper}>
        <div
          id="dropzone"
          style={{ cursor: 'default', borderRadius: '0.25rem' }}
          {...getRootProps()}>
          <div className={styles.scrollableWrapper} ref={dropzoneRef}>
            <input id="file-input" {...getInputProps()} />
          </div>
          {/* @ts-ignore Styled JSX */}
          <style jsx>
            {`
              .dropzone {
                width: ${files.length > 0 ? '90%' : '100%'};
              }
            `}
          </style>
        </div>
        <aside
          ref={thumbnailRef}
          className={styles.thumbsContainer}
          onDrop={e => handleDrop(e)}>
          <DraggableWrapper
            files={files}
            moveFiles={moveFiles}
            deleteFile={deleteFile}
          />
        </aside>
      </div>
    </GlobalHotKeys>
  )
}
Example #24
Source File: OpenFileButton.tsx    From pybricks-code with MIT License 4 votes vote down vote up
OpenFileButton: React.VoidFunctionComponent<OpenFileButtonProps> = ({
    id,
    label,
    mimeType,
    fileExtension,
    tooltip,
    icon,
    enabled,
    showProgress,
    progress,
    onFile,
    onReject,
    onClick,
}) => {
    const [isSmallScreen, setIsSmallScreen] = useState(
        window.innerWidth <= smallScreenThreshold,
    );

    useEffect(() => {
        const handleResize = () => {
            setIsSmallScreen(window.innerWidth <= smallScreenThreshold);
        };
        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
    });

    const buttonSize = isSmallScreen ? SpinnerSize.SMALL : SpinnerSize.STANDARD;

    const { getRootProps, getInputProps } = useDropzone({
        accept: { [mimeType]: [fileExtension] },
        multiple: false,
        noClick: onClick !== undefined,
        onDropAccepted: (acceptedFiles) => {
            // should only be one file since multiple={false}
            acceptedFiles.forEach((f) => {
                const reader = new FileReader();

                reader.onabort = (): void => console.error('file reading was aborted');
                reader.onerror = (): void => console.error('file reading has failed');
                reader.onload = (): void => {
                    const binaryStr = reader.result;
                    if (binaryStr === null) {
                        throw Error('Unexpected null binaryStr');
                    }
                    if (typeof binaryStr === 'string') {
                        throw Error('Unexpected string binaryStr');
                    }
                    onFile(binaryStr);
                };
                reader.readAsArrayBuffer(f);
            });
        },
        onDropRejected: (fileRejections) => {
            // should only be one file since multiple={false}
            fileRejections.forEach((r) => {
                onReject(r.file);
            });
        },
    });

    const { toolbarItemFocusProps, excludeFromTabOrder } = useToolbarItemFocus({ id });

    return (
        <Tooltip2
            content={tooltip}
            placement="bottom"
            hoverOpenDelay={tooltipDelay}
            renderTarget={({
                ref: tooltipTargetRef,
                isOpen: _tooltipIsOpen,
                ...tooltipTargetProps
            }) => (
                <Button
                    {...getRootProps({
                        id,
                        'aria-label': label,
                        refKey: 'elementRef',
                        elementRef: tooltipTargetRef as IRef<HTMLButtonElement>,
                        ...tooltipTargetProps,
                        // https://github.com/palantir/blueprint/pull/5300
                        'aria-haspopup': undefined,
                        intent: Intent.PRIMARY,
                        disabled: enabled === false,
                        onClick,
                        ...toolbarItemFocusProps,
                        tabIndex: excludeFromTabOrder ? -1 : 0,
                        // HACK: work around useDropZone "feature" even though
                        // this role is already implicit on buttons
                        role: 'button',
                    })}
                >
                    <input {...getInputProps()} />
                    {showProgress ? (
                        <Spinner
                            value={progress}
                            intent={Intent.PRIMARY}
                            size={buttonSize}
                        />
                    ) : (
                        <img
                            aria-hidden={true}
                            width={`${buttonSize}px`}
                            height={`${buttonSize}px`}
                            src={icon}
                        />
                    )}
                </Button>
            )}
        />
    );
}
Example #25
Source File: index.tsx    From rabet-extension with GNU General Public License v3.0 4 votes vote down vote up
ImportBackupFile = ({
  isModal,
  onCancel,
  onSubmit,
  usage,
}: ImportBackupFileType) => {
  const [fileContent, setFileContent] = useState('');

  const onDrop = useCallback((acceptedFiles) => {
    const file = acceptedFiles[0];

    const reader = new FileReader();

    reader.onabort = () => {};
    reader.onerror = () => {};
    reader.onload = () => {
      const binaryStr = reader.result;
      const enc = new TextDecoder('utf-8');
      const text = enc.decode(binaryStr);

      setFileContent(text);
    };
    reader.readAsArrayBuffer(file);
  }, []);

  const { acceptedFiles, getRootProps, getInputProps } = useDropzone({
    onDrop,
    accept: 'text/plain',
    maxFiles: 1,
  });

  const handleCancel = (form: any) => {
    form.reset();

    onCancel();
  };

  const handleSubmitFunc = async (values: FormValues) => {
    if (!fileContent) {
      return {
        key: 'Uploaded file has no content',
      };
    }

    let data;

    try {
      data = JSON.parse(decrypt(values.key, fileContent));
    } catch (e) {
      return {
        key: 'Could not decrypt the specified file.',
      };
    }

    await addBackupAccountsAction(data);

    onSubmit();

    return {};
  };

  const validateForm = (values: FormValues) => {
    const errors = {} as FormValues;

    if (!values.key) {
      errors.key = '';
    } else if (values.key.length < 10) {
      errors.key = 'Invalid key';
    }

    return errors;
  };

  const isUploaded = acceptedFiles && acceptedFiles.length;

  return (
    <div>
      <S.MediaBtn {...getRootProps()}>
        <input {...getInputProps()} />
        <Button
          type="file"
          variant="outlined"
          size="medium"
          content={
            isUploaded ? acceptedFiles[0].name : 'Select backup file'
          }
          startIcon={isUploaded ? '' : <Download />}
          style={{
            borderRadius: '4px',
            marginTop: '5px',
          }}
        />
      </S.MediaBtn>

      {isUploaded ? (
        <Form
          onSubmit={handleSubmitFunc}
          validate={validateForm}
          render={({ submitError, handleSubmit, form, pristine }) => (
            <form
              className="form"
              onSubmit={handleSubmit}
              autoComplete="off"
            >
              <Field name="key">
                {({ input, meta }) => (
                  <S.InputContainer>
                    <label className="label-primary">Key</label>
                    <Input
                      type="text"
                      size="medium"
                      placeholder="Enter your key"
                      input={input}
                      meta={meta}
                      autoFocus
                    />
                  </S.InputContainer>
                )}
              </Field>
              {submitError && <Error>{submitError}</Error>}
              {isModal || usage === 'extension' ? (
                <ButtonContainer
                  btnSize={100}
                  justify="end"
                  mt={usage === 'extension' ? 170 : 60}
                  gap={7}
                >
                  <Button
                    variant="default"
                    size="medium"
                    content="Cancel"
                    onClick={() => {
                      handleCancel(form);
                    }}
                  />
                  <Button
                    type="submit"
                    variant="primary"
                    size="medium"
                    content="Import"
                    disabled={pristine}
                  />
                </ButtonContainer>
              ) : (
                <S.ButtonContainer>
                  <Button
                    type="submit"
                    variant="primary"
                    size="medium"
                    content="Import"
                    disabled={pristine}
                  />
                  <S.SecondButton>
                    <Button
                      variant="default"
                      size="medium"
                      content="Back"
                      onClick={() => {
                        handleCancel(form);
                      }}
                      startIcon={<ArrowBack />}
                    />
                  </S.SecondButton>
                </S.ButtonContainer>
              )}
            </form>
          )}
        />
      ) : (
        ''
      )}
    </div>
  );
}
Example #26
Source File: AttachmentUploader.tsx    From AttachmentUploader with MIT License 4 votes vote down vote up
AttachmentUploader: React.FC<UploadProps> = (uploadProps: UploadProps) => {
    
    const [uploadIcn,setuploadIcn]=React.useState(uploadProps.uploadIcon);
    const [totalFileCount, setTotalFileCount] = React.useState(0);
    const [currentUploadCount, setCurrentUploadCount] = React.useState(0);
    const translate = (name:string) => uploadProps.context?.resources.getString(name);

    const onDrop = React.useCallback((acceptedFiles:any) => {
       
        if(acceptedFiles && acceptedFiles.length){
            setTotalFileCount(acceptedFiles.length);
        }
       

        const toBase64 = async (file:any) => new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = () => resolve(reader.result);
            reader.onabort = () => reject();
            reader.onerror = error => reject(error);
        });

        const uploadFileToRecord = async (id: string, entity: string,entitySetName:string,
        fileInfo:  FileInfo,context: ComponentFramework.Context<IInputs>)=>{

            let isActivityMimeAttachment = (entity.toLowerCase() === "email" || entity.toLowerCase() === "appointment");
            let attachmentRecord: ComponentFramework.WebApi.Entity = {};
            if (isActivityMimeAttachment) {
                attachmentRecord["[email protected]"] = `/activitypointers(${id})`;
                attachmentRecord["body"] = fileInfo.body;
            }
            else {
                attachmentRecord[`objectid_${entity}@odata.bind`] = `/${entitySetName}(${id})`;
                attachmentRecord["documentbody"] = fileInfo.body;
            }
       
            if(fileInfo.type && fileInfo.type!==""){
              attachmentRecord["mimetype"] =fileInfo.type;
            }

            attachmentRecord["filename"] = fileInfo.name;
            attachmentRecord["objecttypecode"] = entity;
            let attachmentEntity = isActivityMimeAttachment ? "activitymimeattachment" : "annotation";
            await context.webAPI.createRecord(attachmentEntity, attachmentRecord)
        }


        const uploadFilesToCRM = async (files: any) => {

         
          try{
            for(let i=0;i<acceptedFiles.length;i++)
            {
                setCurrentUploadCount(i);
                let file=acceptedFiles[i] as any;
                let base64Data=await toBase64(acceptedFiles[i]);
                let base64DataStr=base64Data as string;
                let base64IndexOfBase64 = base64DataStr.indexOf(';base64,') + ';base64,'.length;
                var base64 = base64DataStr.substring(base64IndexOfBase64);
                let fileInfo:FileInfo ={name:file.name,type:file.type,body:base64};
              let entityId = uploadProps.id;
              let entityName = uploadProps.entityName;

              if (entityId == null || entityId === "") {//this happens when the record is created and the user tries to upload
                let currentPageContext = uploadProps.context as any;
                currentPageContext = currentPageContext ? currentPageContext["page"] : undefined;
                entityId = currentPageContext.entityId;
                entityName = currentPageContext.entityTypeName;
              }

                await uploadFileToRecord(entityId,entityName,uploadProps.entitySetName, fileInfo,uploadProps.context!!);
            }
          }
          catch(e: any){         
            let errorMessagePrefix=(acceptedFiles.length===1) ? translate("error_while_uploading_attachment") : translate("error_while_uploading_attachments");
            let errOptions={message:`${errorMessagePrefix} ${e.message}`};
            uploadProps.context?.navigation.openErrorDialog(errOptions)
          }

            setTotalFileCount(0);
            let xrmObj: any = (window as any)["Xrm"];
            if (xrmObj && xrmObj.Page && uploadProps.controlToRefresh) {
                var controlToRefresh = xrmObj.Page.getControl(uploadProps.controlToRefresh);
                if (controlToRefresh) {
                    controlToRefresh.refresh();
                }
            }
        }


        uploadFilesToCRM(acceptedFiles);




      }, [totalFileCount,currentUploadCount])
  
        const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop })

  
    if (uploadProps.id==null ||uploadProps.id === "") {
      return (
<div className={"defaultContentCont"}>
{translate("save_record_to_enable_content")}
</div>
      );
    }

        let fileStats = null;
        if (totalFileCount > 0) {
          fileStats = (          
            <div className={"filesStatsCont uploadDivs"}>
              <div>
                <FontAwesomeIcon icon={faSpinner} inverse size="2x" spin />
              </div>
              <div className={"uploadStatusText"}>
              {translate("uploading")} ({currentUploadCount}/{totalFileCount})
             </div>
            </div>
          );
        }      
      


        return (
          <div className={"dragDropCont"}>
            <div className={"dropZoneCont uploadDivs"} {...getRootProps()} style={{ backgroundColor: isDragActive ? '#F8F8F8' : 'white' }}>
              <input {...getInputProps()} />
              <div>
                <img className={"uploadImgDD"} src={uploadIcn} alt="Upload" />                
              </div>
              <div>
                {
                  isDragActive ?
                    <p>{translate("drop_files_here")}</p> :
                    <p>{translate("drop_files_here_or_click_to_upload")}</p>
                }
              </div>
            </div>
      
            { fileStats }
      
        

          </div>
        )
      


}
Example #27
Source File: index.tsx    From globe-3d with MIT License 4 votes vote down vote up
Home = () => {
  const [globeFile, setGlobeFile] = React.useState(null);
  const router = useRouter();
  const [imageUrl, setImageUrl] = React.useState("/images/texture.png");
  const globeRef: any = React.useRef(null);
  const inputRef: any = React.useRef(null);
  const linkRef: any = React.useRef(null);
  const arcsData = [1, 2, 3, 4, 5, 6].map(() => ({
    startLat: (Math.random() - 0.5) * 180,
    startLng: (Math.random() - 0.5) * 360,
    endLat: (Math.random() - 0.5) * 180,
    endLng: (Math.random() - 0.5) * 360,
    color: [["#000000"][0], ["#000000"][0]],
  }));

  const processFile = (files) => {
    const data = URL.createObjectURL(files[0]);
    setImageUrl(data);
    setGlobeFile(files[0]);
  };

  const onDrop = React.useCallback((files) => {
    processFile(files);
  }, []);
  const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });

  return (
    <div className="">
      <Head>
        <title>Globe 3D</title>
      </Head>

      <main className="" {...getRootProps()}>
        <div className="relative bg-white overflow-hidden min-h-screen">
          <nav className="bg-white">
            <div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
              <div className="border-b border-gray-100">
                <div className="flex items-center justify-between h-16 px-4 sm:px-0">
                  <div className="flex items-center">
                    <div className="flex-shrink-0 text-gray-900">
                      <a href="/">
                        <img
                          className="h-8 w-8"
                          src="/images/logoglobe.png"
                          alt="Globe 3D"
                        />
                      </a>
                    </div>
                    <div className="hidden md:flex flex-1">
                      <div className="ml-10 flex items-baseline space-x-4 justify-end items-end">
                        <a
                          href="https://figma.com/@sonny"
                          className="text-gray-900 hover:bg-blue-400 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
                        >
                          All Plugins
                        </a>
                        <a
                          href="https://twitter.com/sonnylazuardi"
                          className="text-gray-900 hover:bg-blue-400 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
                        >
                          Twitter
                        </a>
                        <a
                          href="https://github.com/sonnylazuardi/globe-3d"
                          className="text-gray-900 hover:bg-blue-400 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
                        >
                          Github
                        </a>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </nav>
          <main className="mt-16 pb-16">
            <div className="mx-auto max-w-7xl">
              <div className="lg:grid lg:grid-cols-12 lg:gap-8">
                <div className="px-4 sm:px-6 sm:text-center md:max-w-2xl md:mx-auto lg:col-span-7 lg:text-left lg:flex lg:items-center">
                  <div>
                    <h1 className="mt-4 text-4xl tracking-tight font-extrabold text-black sm:mt-5 sm:leading-none lg:mt-6 lg:text-5xl xl:text-6xl">
                      <span className="md:block">Convert your design to</span>{" "}
                      <span className="font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-pink-500 md:block">
                        3D Globe
                      </span>
                    </h1>
                    <p className="mt-3 text-base text-gray-900 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
                      Create an Interactive 3D globe based on your flat world
                      map design. Try the figma plugin or upload an image to see
                      it live in action.
                    </p>
                    <p className="mt-8 text-sm text-white uppercase tracking-wide font-semibold sm:mt-10 lg:justify-start md:justify-center flex flex-wrap">
                      <a
                        href="https://www.figma.com/community/plugin/977567760148608604/Globe-3D"
                        className="mr-5 inline-flex items-center px-6 py-3 border bg-gradient-to-r from-blue-300 to-blue-500 hover:from-pink-500 hover:to-orange-500 text-white font-semibold rounded-md transition dutation-150 ease-in-out transform hover:scale-105"
                      >
                        <CloudDownloadIcon className="h-5 w-5 text-white mr-2" />
                        Figma Plugin
                      </a>
                      <a
                        href="https://github.com/sonnylazuardi/globe-3d"
                        className="mr-5 inline-flex items-center px-6 py-3 border text-blue-500 font-semibold rounded-md transition dutation-150 ease-in-out transform hover:scale-105"
                      >
                        <CodeIcon className="h-5 w-5 text-blue-500 mr-2" />
                        Github
                      </a>
                    </p>
                    <div className="py-6">
                      <p className="text-xs cursor-pointer hover:underline leading-5 text-gray-500">
                        This project is free and open source and built for fun.
                      </p>
                      <p className="text-xs leading-5 text-gray-500">
                        Support the creator by giving star on github and a
                        follow on twitter.
                      </p>
                    </div>
                  </div>
                </div>
                <div className="mt-16 sm:mt-24 lg:mt-0 lg:col-span-5 mx-auto px-5 relative">
                  <div className="absolute left-0 top-72 flex z-50">
                    <div className="py-8 px-8 rounded-xl bg-white border border-gray-100 shadow-xl bg-opacity-25 flex flex-col">
                      <button
                        onClick={() => {
                          inputRef?.current.click();
                        }}
                        type="button"
                        className="inline-flex justify-center items-center px-6 py-3 border text-blue-500 font-semibold rounded-md transition dutation-150 ease-in-out transform hover:scale-105 bg-white mb-2"
                      >
                        <input
                          ref={inputRef}
                          onChange={(e) => processFile(e.target.files)}
                          type="file"
                          className="hidden"
                        />
                        <CloudUploadIcon className="h-5 w-5 text-blue-500 mr-2" />
                        Upload or Drag & Drop Image
                      </button>

                      {globeFile ? (
                        <>
                          <button
                            onClick={() => {
                              const canvas =
                                globeRef.current.renderer().domElement;
                              const link = linkRef.current;
                              link.setAttribute("download", "globe.png");
                              link.setAttribute(
                                "href",
                                canvas
                                  .toDataURL("image/png")
                                  .replace("image/png", "image/octet-stream")
                              );
                              link.click();
                            }}
                            type="button"
                            className="inline-flex justify-center items-center px-6 py-3 border text-blue-500 font-semibold rounded-md transition dutation-150 ease-in-out transform hover:scale-105 bg-white mb-2"
                          >
                            <CloudDownloadIcon className="h-5 w-5 text-blue-500 mr-2" />
                            Download Image
                          </button>
                          <button
                            onClick={async () => {
                              if (globeFile) {
                                const id = makeid(8);
                                const toastId = toast.loading(
                                  "Creating your globe URL"
                                );
                                const { data, error } = await supabase.storage
                                  .from("globe")
                                  .upload(`public/${id}.png`, globeFile);
                                if (!error) {
                                  console.log({ data });
                                  toast.success("Your globe URL is Ready", {
                                    id: toastId,
                                  });
                                  router.push(`share/${id}`);
                                }
                              }
                            }}
                            type="button"
                            className="inline-flex justify-center items-center px-6 py-3 border text-blue-500 font-semibold rounded-md transition dutation-150 ease-in-out transform hover:scale-105 bg-white mb-2"
                          >
                            <ShareIcon className="h-5 w-5 text-blue-500 mr-2" />
                            Share Globe
                          </button>
                        </>
                      ) : null}
                    </div>
                  </div>
                  <Globe
                    //@ts-ignore
                    ref={globeRef}
                    width={480}
                    height={480}
                    backgroundColor={"rgba(0,0,0,0)"}
                    globeImageUrl={imageUrl}
                    arcColor={"color"}
                    arcsData={arcsData}
                    arcDashGap={0.6}
                    arcDashLength={0.3}
                    arcDashAnimateTime={4000 + 500}
                    rendererConfig={{ preserveDrawingBuffer: true }}
                  />
                  <a className="hidden" ref={linkRef} />
                </div>
              </div>
            </div>
            <div className="mt-24">
              <h2 className="text-gray-700 text-center text-3xl font-bold">
                Quick Figma Plugin Demo
              </h2>
              <div className="pt-10">
                <div
                  style={{ width: 580, maxWidth: "100%" }}
                  className="mx-auto p-4"
                >
                  <TweetEmbed
                    id="1395404831116849160"
                    options={{
                      theme: "light",
                      conversation: "none",
                      width: 580,
                    }}
                  />
                </div>
              </div>
            </div>
            <div className="mt-24">
              <h2 className="text-gray-700 text-center text-3xl font-bold">
                How is it possible?
              </h2>
              <div className="p-4 text-center">
                This project is powered by React Globe GL
              </div>
              <div className="">
                <div
                  style={{ width: 580, maxWidth: "100%" }}
                  className="mx-auto p-4"
                >
                  <TweetEmbed
                    id="1396007498134417410"
                    options={{
                      theme: "light",
                      conversation: "none",
                      width: 580,
                    }}
                  />
                </div>
              </div>
            </div>
          </main>
          <footer className="bg-gradient-to-r from-blue-300 to-blue-500">
            <div className="max-w-7xl mx-auto py-12 px-4 overflow-hidden sm:px-6 lg:px-8">
              <nav
                className="-mx-5 -my-2 flex flex-wrap justify-center"
                aria-label="Footer"
              >
                <div className="px-5 py-2">
                  <a
                    href="/"
                    className="text-base text-gray-200 hover:text-gray-100"
                  >
                    Home
                  </a>
                </div>
                <div className="px-5 py-2">
                  <a
                    href="https://twitter.com/sonnylazuardi"
                    className="text-base text-gray-200 hover:text-gray-100"
                  >
                    Twitter
                  </a>
                </div>
                <div className="px-5 py-2">
                  <a
                    href="https://github.com/sonnylazuardi/globe-3d"
                    className="text-base text-gray-200 hover:text-gray-100"
                  >
                    Github
                  </a>
                </div>
              </nav>
              <p className="mt-8 text-center text-base text-white">
                © 2021 Sonny Lazuardi. All rights reserved.
              </p>
            </div>
          </footer>
        </div>
        {isDragActive ? (
          <div className="fixed top-0 left-0 right-0 bottom-0 bg-black bg-opacity-50 flex justify-center items-center ">
            <p className="text-3xl text-white text-center font-bold">
              Drop the file here ...
            </p>
          </div>
        ) : null}
      </main>

      <footer className=""></footer>
      <Toaster />
    </div>
  );
}
Example #28
Source File: TableCell.tsx    From firetable with Apache License 2.0 4 votes vote down vote up
export default function Image_({
  column,
  row,
  value,
  onSubmit,
  disabled,
}: IHeavyCellProps) {
  const { tableState, updateCell } = useFiretableContext();
  const { requestConfirmation } = useConfirmation();
  const classes = useStyles({ rowHeight: tableState?.config?.rowHeight ?? 44 });

  const { uploaderState, upload, deleteUpload } = useUploader();
  const { progress, isLoading } = uploaderState;

  // Store a preview image locally while uploading
  const [localImage, setLocalImage] = useState<string>("");

  const onDrop = useCallback(
    (acceptedFiles) => {
      const imageFile = acceptedFiles[0];

      if (imageFile) {
        upload({
          docRef: row.ref,
          fieldName: column.key as string,
          files: [imageFile],
          previousValue: value,
          onComplete: (newValue) => {
            if (updateCell) updateCell(row.ref, column.key, newValue);
            setLocalImage("");
          },
        });
        setLocalImage(URL.createObjectURL(imageFile));
      }
    },
    [value]
  );

  const handleDelete = (ref: string) => () => {
    const newValue = [...value];
    const index = _findIndex(newValue, ["ref", ref]);
    const toBeDeleted = newValue.splice(index, 1);
    toBeDeleted.length && deleteUpload(toBeDeleted[0]);
    onSubmit(newValue);
  };

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    multiple: false,
    accept: IMAGE_MIME_TYPES,
  });

  const dropzoneProps = getRootProps();

  let thumbnailSize = "100x100";
  if (tableState?.config?.rowHeight) {
    if (tableState!.config!.rowHeight! > 50) thumbnailSize = "200x200";
    if (tableState!.config!.rowHeight! > 100) thumbnailSize = "400x400";
  }

  return (
    <Grid
      container
      className={clsx(
        "cell-collapse-padding",
        classes.root,
        isDragActive && classes.dragActive
      )}
      wrap="nowrap"
      alignItems="center"
      spacing={1}
      {...dropzoneProps}
      onClick={undefined}
    >
      <input {...getInputProps()} />

      <Grid item xs className={classes.imglistContainer}>
        <Grid container spacing={1} wrap="nowrap">
          {Array.isArray(value) &&
            value.map((file: FileValue) => (
              <Grid item key={file.downloadURL}>
                {disabled ? (
                  <Tooltip title="Click to open">
                    <ButtonBase
                      className={classes.img}
                      onClick={() => window.open(file.downloadURL, "_blank")}
                    >
                      <Thumbnail
                        imageUrl={file.downloadURL}
                        size={thumbnailSize}
                        objectFit="contain"
                        className={classes.thumbnail}
                      />
                      <Grid
                        container
                        justify="center"
                        alignItems="center"
                        className={classes.deleteImgHover}
                      >
                        {disabled ? (
                          <OpenIcon />
                        ) : (
                          <DeleteIcon color="inherit" />
                        )}
                      </Grid>
                    </ButtonBase>
                  </Tooltip>
                ) : (
                  <Tooltip title="Click to delete">
                    <div>
                      <ButtonBase
                        className={classes.img}
                        onClick={() => {
                          requestConfirmation({
                            title: "Delete Image",
                            body: "Are you sure you want to delete this image?",
                            confirm: "Delete",
                            handleConfirm: handleDelete(file.ref),
                          });
                        }}
                      >
                        <Thumbnail
                          imageUrl={file.downloadURL}
                          size={thumbnailSize}
                          objectFit="contain"
                          className={classes.thumbnail}
                        />
                        <Grid
                          container
                          justify="center"
                          alignItems="center"
                          className={classes.deleteImgHover}
                        >
                          <DeleteIcon color="inherit" />
                        </Grid>
                      </ButtonBase>
                    </div>
                  </Tooltip>
                )}
              </Grid>
            ))}

          {localImage && (
            <Grid item>
              <div
                className={clsx(classes.img, classes.localImgPreview)}
                style={{ backgroundImage: `url("${localImage}")` }}
              />
            </Grid>
          )}
        </Grid>
      </Grid>

      <Grid item className={classes.endButtonContainer}>
        {!isLoading ? (
          !disabled && (
            <IconButton
              size="small"
              className="row-hover-iconButton"
              onClick={(e) => {
                dropzoneProps.onClick!(e);
                e.stopPropagation();
              }}
              color="inherit"
            >
              <AddIcon />
            </IconButton>
          )
        ) : (
          <CircularProgress
            size={24}
            variant={progress === 0 ? "indeterminate" : "static"}
            value={progress}
            thickness={4.6}
            className={classes.circularProgress}
          />
        )}
      </Grid>
    </Grid>
  );
}
Example #29
Source File: File.tsx    From project-papua with Apache License 2.0 4 votes vote down vote up
File: React.FC<Props> = (props) => {
  const { question } = props
  const { values, setValue, translateByID } = useContext(FormContext)
  const value = values[question.id] as FileValues | undefined

  const onDrop = async (acceptedFiles: File[]) => {
    const files = await Promise.all(
      acceptedFiles.map((file) => {
        return new Promise<FileValue | undefined>((resolve) => {
          const reader = new FileReader()

          reader.onerror = () => {
            resolve()
          }
          reader.onabort = () => {
            resolve()
          }
          // onload fired only after the read operation has finished
          reader.onload = () => {
            resolve({
              name: file.name,
              type: file.type,
              size: file.size,
              contents: encode(reader.result as ArrayBuffer),
            })
          }
          reader.readAsArrayBuffer(file)
        })
      })
    )

    setValue(question, [...(value || []), ...(files.filter((f) => !!f) as FileValue[])])
  }

  const { getRootProps, getInputProps, isDragActive, isFocused } = useDropzone({
    minSize: 100, // arbitrary min > 0 (100B)
    maxSize: 4194304, // 4MB
    // This handler is fired both on valid and invalid files.
    onDrop,
    // Accept PNGs, JPGs and PDFs
    accept: ['image/png', 'image/jpeg', 'image/jpg', 'application/pdf'],
  })

  const onRemove = () => {
    setValue(question, undefined)
  }

  const color = isDragActive || isFocused ? '#4776F6' : '#CCCCCC'

  return (
    <>
      <Box
        pad="medium"
        gap="small"
        alignContent="center"
        align="center"
        style={{
          outline: `2px dashed ${color}`,
          cursor: 'pointer',
        }}
        background={{
          color: '#F6F7F9',
        }}
        className="file-upload-box"
        margin={{ bottom: '12px' }}
        {...getRootProps()}
      >
        <input {...getInputProps()} />
        <Paragraph margin={{ vertical: 'none' }} color="black">
          {translateByID('file-uploader-drag-drop')}
        </Paragraph>
        <Paragraph margin={{ vertical: 'none' }} color="black">
          {translateByID('file-uploader-or')}
        </Paragraph>
        <Paragraph margin={{ vertical: 'none' }} color="#4776F6" style={{ display: 'flex', fontWeight: 600 }}>
          {translateByID('file-uploader-choose-file')}
          <CircleIcon color="#4776F6" margin={{ left: '6px' }}>
            <FormNextLink color="white" className="file-upload-icon" />
          </CircleIcon>
        </Paragraph>
      </Box>
      {value &&
        value.map((v, i) => (
          <Box direction="row" pad="medum" height="75px" key={i} align="center" justify="between">
            <Box direction="row">
              {/* TODO: use other SVGs for PDF/JPEG/etc. when those are available */}
              <Image src="/file.jpg.svg" width="45px" />
              <Paragraph margin={{ left: '12px', bottom: '12px' }}>{v.name}</Paragraph>
            </Box>
            <Box direction="row" align="center">
              <Paragraph margin={{ vertical: 'none', right: '12px' }}>
                {translateByID('file-uploader-uploaded')}
              </Paragraph>
              <CircleIcon color="#4776F6">
                <Checkmark color="white" style={{ width: '12px', height: '12px' }} />
              </CircleIcon>
              <Button
                icon={<FormClose />}
                onClick={onRemove}
                size="small"
                margin={{ left: '20px' }}
                style={{
                  borderRadius: '20px',
                  padding: '3px',
                }}
                primary={true}
                color="#eee"
                hoverIndicator={{
                  color: 'lightgrey',
                }}
              />
            </Box>
          </Box>
        ))}
    </>
  )
}