apple2js/js/components/DiskDragTarget.tsx

122 lines
3.8 KiB
TypeScript

import {
BLOCK_FORMATS,
DISK_FORMATS,
DriveNumber,
FLOPPY_FORMATS,
MassStorage,
} from 'js/formats/types';
import { h, JSX, RefObject } from 'preact';
import { useEffect, useRef } from 'preact/hooks';
import { loadLocalFile } from './util/files';
import { spawn } from './util/promises';
export interface DiskDragTargetProps<T>
extends JSX.HTMLAttributes<HTMLDivElement> {
storage: MassStorage<T> | undefined;
driveNo?: DriveNumber;
formats: typeof FLOPPY_FORMATS | typeof BLOCK_FORMATS | typeof DISK_FORMATS;
dropRef?: RefObject<HTMLElement>;
onError: (error: unknown) => void;
}
export const DiskDragTarget = ({
storage,
driveNo,
dropRef,
formats,
onError,
children,
...props
}: DiskDragTargetProps<unknown>) => {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const div = dropRef?.current || ref.current;
if (div) {
const onDragOver = (event: DragEvent) => {
event.preventDefault();
const dt = event.dataTransfer;
if (dt) {
if (
Array.from(dt.items).every(
(item) => item.kind === 'file'
)
) {
dt.dropEffect = 'copy';
} else {
dt.dropEffect = 'none';
}
}
};
const onDragEnd = (event: DragEvent) => {
const dt = event.dataTransfer;
if (dt?.items) {
for (let i = 0; i < dt.items.length; i++) {
dt.items.remove(i);
}
} else {
dt?.clearData();
}
};
const onDrop = (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
const targetDrive = driveNo ?? 1; //TODO(whscullin) Maybe pick available drive
const dt = event.dataTransfer;
if (dt?.files.length === 1 && storage) {
spawn(async () => {
try {
await loadLocalFile(
storage,
formats,
targetDrive,
dt.files[0]
);
} catch (e) {
onError(e);
}
});
} else if (dt?.files.length === 2 && storage) {
spawn(async () => {
try {
await loadLocalFile(
storage,
formats,
1,
dt.files[0]
);
await loadLocalFile(
storage,
formats,
2,
dt.files[1]
);
} catch (e) {
onError(e);
}
});
}
};
div.addEventListener('dragover', onDragOver);
div.addEventListener('dragend', onDragEnd);
div.addEventListener('drop', onDrop);
return () => {
div.removeEventListener('dragover', onDragOver);
div.removeEventListener('dragend', onDragEnd);
div.removeEventListener('drop', onDrop);
};
}
}, [driveNo, dropRef, formats, onError, storage]);
return (
<div ref={ref} {...props}>
{children}
</div>
);
};