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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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}
</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 |
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 |
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 |
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 |
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:
<strong>{bucketInfo?.creation_date || ""}</strong>
</span>
<span className={classes.detailsSpacer}>
Access:
<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} 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 |
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 |
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 |
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 |
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 |
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 |
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 |
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>
))}
</>
)
}