mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
yes
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@opencut/hooks",
|
||||
"version": "0.0.0",
|
||||
"description": "Hooks package for OpenCut",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./use-file-upload": "./src/use-file-upload.ts",
|
||||
"./use-file-paste": "./src/use-file-paste.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/react": "^19.2.7",
|
||||
"react": "^19.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./use-file-upload";
|
||||
export * from "./use-file-paste";
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
interface UseFilePasteOptions {
|
||||
onFilesPaste: (files: File[]) => void;
|
||||
}
|
||||
|
||||
export function useFilePaste({ onFilesPaste }: UseFilePasteOptions) {
|
||||
useEffect(() => {
|
||||
function handlePaste(e: ClipboardEvent) {
|
||||
if (!e.clipboardData?.files.length) return;
|
||||
|
||||
const files = Array.from(e.clipboardData.files);
|
||||
if (files.length > 0) {
|
||||
e.preventDefault();
|
||||
onFilesPaste(files);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("paste", handlePaste);
|
||||
return () => window.removeEventListener("paste", handlePaste);
|
||||
}, [onFilesPaste]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { useState, useRef } from "react";
|
||||
|
||||
interface UseFileUploadOptions {
|
||||
accept?: string;
|
||||
multiple?: boolean;
|
||||
onFilesSelected?: (files: FileList) => void;
|
||||
}
|
||||
|
||||
function containsFiles(dataTransfer: DataTransfer): boolean {
|
||||
const isInternalDrag = dataTransfer.types.includes("application/x-media-item");
|
||||
const hasFiles = dataTransfer.types.includes("Files");
|
||||
|
||||
return !isInternalDrag && hasFiles;
|
||||
}
|
||||
|
||||
export function useFileUpload({ accept, multiple, onFilesSelected }: UseFileUploadOptions = {}) {
|
||||
const [isDragOver, setIsDragOver] = useState(false);
|
||||
const dragCounterRef = useRef(0);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
function openFilePicker() {
|
||||
if (!inputRef.current) return;
|
||||
|
||||
inputRef.current.accept = accept || "*";
|
||||
inputRef.current.multiple = multiple || false;
|
||||
inputRef.current.click();
|
||||
}
|
||||
|
||||
function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const files = event.target.files;
|
||||
if (files && files.length > 0 && onFilesSelected) {
|
||||
onFilesSelected(files);
|
||||
}
|
||||
|
||||
if (event.target) {
|
||||
event.target.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragEnter(e: React.DragEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!containsFiles(e.dataTransfer)) return;
|
||||
|
||||
dragCounterRef.current += 1;
|
||||
setIsDragOver(true);
|
||||
}
|
||||
|
||||
function handleDragOver(e: React.DragEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!containsFiles(e.dataTransfer)) return;
|
||||
}
|
||||
|
||||
function handleDragLeave(e: React.DragEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!containsFiles(e.dataTransfer)) return;
|
||||
|
||||
dragCounterRef.current -= 1;
|
||||
if (dragCounterRef.current === 0) {
|
||||
setIsDragOver(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrop(e: React.DragEvent) {
|
||||
e.preventDefault();
|
||||
setIsDragOver(false);
|
||||
dragCounterRef.current = 0;
|
||||
|
||||
if (onFilesSelected && containsFiles(e.dataTransfer)) {
|
||||
const files = e.dataTransfer.files;
|
||||
const shouldUseMultiple = multiple ?? false;
|
||||
|
||||
if (shouldUseMultiple) {
|
||||
onFilesSelected(files);
|
||||
} else if (files.length > 0) {
|
||||
const dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(files[0]);
|
||||
onFilesSelected(dataTransfer.files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isDragOver,
|
||||
openFilePicker,
|
||||
fileInputProps: {
|
||||
ref: inputRef,
|
||||
type: "file",
|
||||
style: { display: "none" },
|
||||
onChange: handleFileChange,
|
||||
},
|
||||
dragProps: {
|
||||
onDragEnter: handleDragEnter,
|
||||
onDragOver: handleDragOver,
|
||||
onDragLeave: handleDragLeave,
|
||||
onDrop: handleDrop,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -8,12 +8,12 @@
|
||||
".": "./src/index.ts",
|
||||
"./icons": "./src/icons/index.tsx"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/react": "^19.2.7",
|
||||
"react": "^19.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user