144 lines
4.0 KiB
TypeScript
144 lines
4.0 KiB
TypeScript
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/constants';
|
|
import { WebApplication } from '@/ui_models/application';
|
|
import { AppState } from '@/ui_models/app_state';
|
|
import { StreamingFileReader } from '@standardnotes/filepicker';
|
|
import { SNNote } from '@standardnotes/snjs';
|
|
import { FunctionComponent } from 'preact';
|
|
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
|
import { AttachedFilesPopover } from './AttachedFilesPopover';
|
|
import {
|
|
PopoverFileItemAction,
|
|
PopoverFileItemActionType,
|
|
} from './PopoverFileItemAction';
|
|
|
|
export enum PopoverTabs {
|
|
AttachedFiles,
|
|
AllFiles,
|
|
}
|
|
|
|
export type PopoverWrapperProps = {
|
|
application: WebApplication;
|
|
appState: AppState;
|
|
note: SNNote;
|
|
fileActionHandler: (action: PopoverFileItemAction) => Promise<boolean>;
|
|
};
|
|
|
|
export const PopoverDragNDropWrapper: FunctionComponent<
|
|
PopoverWrapperProps
|
|
> = ({ fileActionHandler, appState, application, note }) => {
|
|
const dropzoneRef = useRef<HTMLDivElement>(null);
|
|
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
const [currentTab, setCurrentTab] = useState(PopoverTabs.AttachedFiles);
|
|
const dragCounter = useRef(0);
|
|
|
|
const handleDrag = (event: DragEvent) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
};
|
|
|
|
const handleDragIn = (event: DragEvent) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
dragCounter.current = dragCounter.current + 1;
|
|
|
|
if (event.dataTransfer?.items.length) {
|
|
setIsDragging(true);
|
|
}
|
|
};
|
|
|
|
const handleDragOut = (event: DragEvent) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
dragCounter.current = dragCounter.current - 1;
|
|
|
|
if (dragCounter.current > 0) {
|
|
return;
|
|
}
|
|
|
|
setIsDragging(false);
|
|
};
|
|
|
|
const handleDrop = useCallback(
|
|
(event: DragEvent) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
setIsDragging(false);
|
|
|
|
if (event.dataTransfer?.items.length) {
|
|
Array.from(event.dataTransfer.items).forEach(async (item) => {
|
|
let fileOrHandle;
|
|
if (StreamingFileReader.available()) {
|
|
fileOrHandle =
|
|
(await item.getAsFileSystemHandle()) as FileSystemFileHandle;
|
|
} else {
|
|
fileOrHandle = item.getAsFile();
|
|
}
|
|
if (fileOrHandle) {
|
|
const uploadedFiles = await appState.files.uploadNewFile(
|
|
fileOrHandle
|
|
);
|
|
if (!uploadedFiles) {
|
|
return;
|
|
}
|
|
|
|
if (currentTab === PopoverTabs.AttachedFiles) {
|
|
uploadedFiles.forEach((file) => {
|
|
fileActionHandler({
|
|
type: PopoverFileItemActionType.AttachFileToNote,
|
|
payload: file,
|
|
});
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
event.dataTransfer.clearData();
|
|
dragCounter.current = 0;
|
|
}
|
|
},
|
|
[appState.files, currentTab, fileActionHandler]
|
|
);
|
|
|
|
useEffect(() => {
|
|
const dropzoneElement = dropzoneRef.current;
|
|
|
|
if (dropzoneElement) {
|
|
dropzoneElement.addEventListener('dragenter', handleDragIn);
|
|
dropzoneElement.addEventListener('dragleave', handleDragOut);
|
|
dropzoneElement.addEventListener('dragover', handleDrag);
|
|
dropzoneElement.addEventListener('drop', handleDrop);
|
|
}
|
|
|
|
return () => {
|
|
dropzoneElement?.removeEventListener('dragenter', handleDragIn);
|
|
dropzoneElement?.removeEventListener('dragleave', handleDragOut);
|
|
dropzoneElement?.removeEventListener('dragover', handleDrag);
|
|
dropzoneElement?.removeEventListener('drop', handleDrop);
|
|
};
|
|
}, [handleDrop]);
|
|
|
|
return (
|
|
<div
|
|
ref={dropzoneRef}
|
|
className="focus:shadow-none"
|
|
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
|
style={{
|
|
border: isDragging ? '2px dashed var(--sn-stylekit-info-color)' : '',
|
|
}}
|
|
>
|
|
<AttachedFilesPopover
|
|
application={application}
|
|
appState={appState}
|
|
note={note}
|
|
fileActionHandler={fileActionHandler}
|
|
currentTab={currentTab}
|
|
setCurrentTab={setCurrentTab}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|