This commit is contained in:
Andreas Penski 2022-11-18 07:21:56 +00:00
parent a10cc14d06
commit 219aeaa1b7
100 changed files with 27369 additions and 1072 deletions

View file

@ -0,0 +1,94 @@
:where(.dropzone) {
--dropzone-color-text: var(--text-faded);
--dropzone-icon-active: var(--text-accent-2);
--dropzone-color-background: var(--surface-2);
--dropzone-color-background-active: var(--surface-accent-0);
--dropzone-color-border: var(--color-border);
--dropzone-color-border-active: var(--color-border-accent);
--dropzone-opacity-hover-preview: 0;
--dropzone-shadow-opacity: 0;
--dropzone-border-size: 0.2rem;
}
:where([data-theme="dark"] .dropzone) {
--dropzone-color-text: var(--text-0);
--dropzone-icon-active: var(--text-accent-0);
--dropzone-color-background: transparent;
--dropzone-color-background-active: var(--surface-accent-5);
}
.dropzone {
position: relative;
cursor: pointer;
width: 100%;
height: 15em;
color: var(--dropzone-color-text);
background: var(--dropzone-color-background);
border: var(--dropzone-border-size) dashed var(--dropzone-color-border);
border-radius: var(--border-radius-medium);
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--ifm-global-shadow-lw);
}
.dropzone::before {
content: "";
position: absolute;
pointer-events: none;
top: 0;
right: 0;
bottom: 0;
left: 0;
box-shadow: var(--ifm-global-shadow-md);
z-index: -1;
opacity: var(--dropzone-shadow-opacity);
transition: opacity 200ms ease;
}
.active {
--dropzone-opacity-hover-preview: 0.8;
--dropzone-shadow-opacity: 1;
}
.hasFiles {
--dropzone-color-background: var(--dropzone-color-background-active);
--dropzone-color-border: var(--dropzone-color-border-active);
--dropzone-shadow-opacity: 0.25;
}
.fileHoverPreview {
--dropzone-color-background: var(--dropzone-color-background-active);
--dropzone-color-border: var(--dropzone-color-border-active);
position: absolute;
top: calc(-1 * var(--dropzone-border-size));
right: calc(-1 * var(--dropzone-border-size));
bottom: calc(-1 * var(--dropzone-border-size));
left: calc(-1 * var(--dropzone-border-size));
display: flex;
justify-content: center;
align-items: center;
color: var(--dropzone-color-text);
background: var(--dropzone-color-background);
border: var(--dropzone-border-size) dashed var(--dropzone-color-border);
border-radius: var(--border-radius-medium);
opacity: var(--dropzone-opacity-hover-preview);
transition: opacity 150ms ease-in-out;
}
.icon {
font-size: 3rem;
width: 1em;
height: 1em;
}
.fileHoverIcon {
font-size: 5rem;
color: var(--dropzone-icon-active);
}
.uploadIcon {
color: var(--dropzone-color-text);
margin-right: 0.5rem;
}

View file

@ -0,0 +1,80 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from "react";
import clsx from "clsx";
import type { DropEvent } from "react-dropzone";
import { useDropzone } from "react-dropzone";
import type { DropzoneProps, RejectionType } from "./types";
import styles from "./Dropzone.module.css";
const Dropzone = ({
accept,
children,
className,
activeClassName,
multiple = false,
name,
onDrop,
hasSelectedFiles,
...props
}: DropzoneProps): JSX.Element => {
const handleDrop = (
accepted: File[],
fileRejections: RejectionType[],
event: DropEvent,
) => {
const rejected = fileRejections.map((rejection) => rejection.file);
onDrop(accepted, rejected, event);
};
const {
getRootProps,
getInputProps,
isDragActive,
isDragAccept,
isDragReject,
} = useDropzone({ accept, multiple, onDrop: handleDrop, ...props });
return (
<div
{...getRootProps()}
className={clsx(
styles.dropzone,
isDragActive && styles.active,
hasSelectedFiles && styles.hasFiles,
className,
isDragActive && activeClassName,
)}
data-testid="dropzone"
data-is-drag-active={isDragActive}
data-is-drag-accepted={isDragAccept}
data-is-drag-rejected={isDragReject}
data-has-files={hasSelectedFiles}
>
<div
className={clsx(
styles.fileHoverPreview,
isDragActive && styles.previewActive,
)}
>
<svg
className={clsx(styles.icon, styles.fileHoverIcon)}
fill="currentColor"
aria-hidden="true"
viewBox="0 0 24 24"
>
<path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z" />
</svg>
</div>
<svg
className={clsx(styles.icon, styles.uploadIcon)}
fill="currentColor"
aria-hidden="true"
viewBox="0 0 24 24"
>
<path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z" />
</svg>
{children}
<input name={name} {...getInputProps()} data-testid="dropzone-input" />
</div>
);
};
export default Dropzone;

View file

@ -0,0 +1,5 @@
import Dropzone from "./Dropzone";
export { default as useDropzone } from "./useDropzone";
export default Dropzone;

View file

@ -0,0 +1,24 @@
import type { ReactNode, RefAttributes } from "react";
import type {
DropEvent,
DropzoneProps as ReactDropzoneProps,
DropzoneRef,
} from "react-dropzone";
import type { ExtendProps } from "../util/types";
export interface RejectionType {
file: File;
}
export type DropzoneProps = ExtendProps<
ReactDropzoneProps & RefAttributes<DropzoneRef>,
{
children?: ReactNode;
className?: string;
activeClassName?: string;
multiple?: boolean;
hasSelectedFiles?: boolean;
name?: string;
onDrop: (accepted: File[], rejections: File[], event: DropEvent) => void;
}
>;

View file

@ -0,0 +1,53 @@
import { useCallback, useMemo, useState } from "react";
interface DropzoneHelpers {
selectedFiles: File[];
rejectedFiles: File[];
hasSelectedFiles: boolean;
getProps: () => {
onDrop: (accepted: File[], rejected: File[]) => void;
multiple: boolean;
accept: string | string[];
hasSelectedFiles: boolean;
};
reset: () => void;
}
function useDropzone({
multiple = false,
accept,
}: {
multiple?: boolean;
accept: string | string[];
}): DropzoneHelpers {
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<File[]>([]);
const hasSelectedFiles = selectedFiles.length > 0;
const getProps = useMemo(() => {
const handleDrop = (accepted: File[], rejected: File[]) => {
setSelectedFiles(accepted);
if (rejected.length === 0) {
setRejectedFiles([]);
} else {
setRejectedFiles(rejected);
}
};
return () => ({
onDrop: handleDrop,
multiple,
accept,
hasSelectedFiles,
});
}, [accept, hasSelectedFiles, multiple]);
const reset = useCallback(() => {
setSelectedFiles([]);
setRejectedFiles([]);
}, []);
return { selectedFiles, rejectedFiles, hasSelectedFiles, getProps, reset };
}
export default useDropzone;