import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; import { open } from '@tauri-apps/plugin-dialog'; import classNames from 'classnames'; import mime from 'mime'; import type { ReactNode } from 'react'; import { useEffect, useRef, useState } from 'react'; import type { ButtonProps } from './core/Button'; import { Button } from './core/Button'; import { IconButton } from './core/IconButton'; import { IconTooltip } from './core/IconTooltip'; import { Label } from './core/Label'; import { HStack } from './core/Stacks'; type Props = Omit & { onChange: (value: { filePath: string | null; contentType: string | null }) => void; filePath: string | null; nameOverride?: string | null; directory?: boolean; inline?: boolean; noun?: string; help?: ReactNode; label?: ReactNode; }; // Special character to insert ltr text in rtl element const rtlEscapeChar = <>‎; export function SelectFile({ onChange, filePath, inline, className, directory, noun, nameOverride, size = 'sm', label, help, ...props }: Props) { const handleClick = async () => { const filePath = await open({ title: directory ? 'Select Folder' : 'Select File', multiple: false, directory, }); if (filePath == null) return; const contentType = filePath ? mime.getType(filePath) : null; onChange({ filePath, contentType }); }; const handleClear = async () => { onChange({ filePath: null, contentType: null }); }; const itemLabel = noun ?? (directory ? 'Folder' : 'File'); const selectOrChange = (filePath ? 'Change ' : 'Select ') + itemLabel; const [isHovering, setIsHovering] = useState(false); const ref = useRef(null); // Listen for dropped files on the element // NOTE: This doesn't work for Windows since native drag-n-drop can't work at the same tmie // as browser drag-n-drop. useEffect(() => { let unlisten: (() => void) | undefined; const setup = async () => { const webview = getCurrentWebviewWindow(); unlisten = await webview.onDragDropEvent((event) => { if (event.payload.type === 'over') { const p = event.payload.position; const r = ref.current?.getBoundingClientRect(); if (r == null) return; const isOver = p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom; console.log('IS OVER', isOver); setIsHovering(isOver); } else if (event.payload.type === 'drop' && isHovering) { console.log('User dropped', event.payload.paths); const p = event.payload.paths[0]; if (p) onChange({ filePath: p, contentType: null }); setIsHovering(false); } else { console.log('File drop cancelled'); setIsHovering(false); } }); }; setup().catch(console.error); return () => { if (unlisten) unlisten(); }; }, [isHovering, onChange]); const filePathWithNameOverride = nameOverride ? `${filePath} (${nameOverride})` : filePath; return (
{label && ( )} {!inline && ( <> {filePath && ( )}
{rtlEscapeChar} {filePath ?? `No ${itemLabel.toLowerCase()} selected`}
{filePath == null && help && !label && } )}
); }