Files
supabase/apps/studio/hooks/ui/useCsvFileDrop.ts
T
CharisandJoshen Lim e8a19faa03 feat: drop file to import data in Table Editor (#37888)
* feat: drop file to import data

In the Table Editor for an empty table, make the entire grid area a
dropzone so that files can be dropped onto the grid area for importing.
Dropping a file opens the SpreadsheetImport side panel with the dropped
file already loaded.

* refactor: drag and drop file import

Refactor logic into hooks and utility functions

* refactor: file drop import

Small changes caught in code review

* Opt to pass file through valtio store instead of event listener

* refactor: telemetry properties + cleaning up hook deps

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2025-08-13 11:32:06 -04:00

62 lines
1.6 KiB
TypeScript

import { type DragEvent, useCallback, useState } from 'react'
import { type ImportDataFileDroppedEvent } from 'common/telemetry-constants'
import { flagInvalidFileImport } from 'components/interfaces/TableGridEditor/SidePanelEditor/SpreadsheetImport/SpreadsheetImport.utils'
interface UseCsvFileDropOptions {
enabled: boolean
onFileDropped: (file: File) => void
onTelemetryEvent?: (eventName: ImportDataFileDroppedEvent['action']) => void
}
interface UseCsvFileDropReturn {
isDraggedOver: boolean
onDragOver: (event: DragEvent<HTMLDivElement>) => void
onFileDrop: (event: DragEvent<HTMLDivElement>) => void
}
export function useCsvFileDrop({
enabled,
onFileDropped,
onTelemetryEvent,
}: UseCsvFileDropOptions): UseCsvFileDropReturn {
const [isDraggedOver, setIsDraggedOver] = useState(false)
const onDragOver = useCallback(
(event: DragEvent<HTMLDivElement>) => {
if (!enabled) return
if (event.type === 'dragover' && !isDraggedOver) {
setIsDraggedOver(true)
} else if (event.type === 'dragleave' || event.type === 'drop') {
setIsDraggedOver(false)
}
event.stopPropagation()
event.preventDefault()
},
[enabled, isDraggedOver]
)
const onFileDrop = useCallback(
(event: DragEvent<HTMLDivElement>) => {
if (!enabled) return
onDragOver(event)
const [file] = event.dataTransfer.files
if (flagInvalidFileImport(file)) return
onFileDropped(file)
onTelemetryEvent?.('import_data_dropzone_file_added')
},
[enabled, onDragOver, onFileDropped, onTelemetryEvent]
)
return {
isDraggedOver,
onDragOver,
onFileDrop,
}
}