Skip to content

Commit 0b5b482

Browse files
committed
feat(web): support drag and drop plugin upload
1 parent e7101e8 commit 0b5b482

1 file changed

Lines changed: 46 additions & 7 deletions

File tree

web/src/pages/admin/PluginsPage.tsx

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useRef, useEffect } from 'react';
1+
import { useState, useRef, useEffect, type DragEvent } from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
44
import { pluginsApi } from '../../shared/api/plugins';
@@ -459,9 +459,11 @@ function InstallPluginModal({
459459
const { t } = useTranslation();
460460
const { toast } = useToast();
461461
const fileInputRef = useRef<HTMLInputElement>(null);
462+
const dragCounterRef = useRef(0);
462463

463464
const [installTab, setInstallTab] = useState<'upload' | 'github'>('upload');
464465
const [selectedFile, setSelectedFile] = useState<File | null>(null);
466+
const [dragActive, setDragActive] = useState(false);
465467
const [pluginName, setPluginName] = useState('');
466468
const [githubRepo, setGithubRepo] = useState('');
467469

@@ -491,14 +493,46 @@ function InstallPluginModal({
491493
setSelectedFile(null);
492494
setPluginName('');
493495
setGithubRepo('');
496+
dragCounterRef.current = 0;
494497
if (fileInputRef.current) fileInputRef.current.value = '';
495498
}
496499

497500
function handleClose() {
498501
resetForm();
502+
setDragActive(false);
499503
onClose();
500504
}
501505

506+
function handleFileSelect(file: File | null) {
507+
setSelectedFile(file);
508+
setDragActive(false);
509+
}
510+
511+
function handleDragEvent(e: DragEvent<HTMLDivElement>) {
512+
e.preventDefault();
513+
e.stopPropagation();
514+
}
515+
516+
function handleDragEnter(e: DragEvent<HTMLDivElement>) {
517+
handleDragEvent(e);
518+
dragCounterRef.current += 1;
519+
setDragActive(true);
520+
}
521+
522+
function handleDragLeave(e: DragEvent<HTMLDivElement>) {
523+
handleDragEvent(e);
524+
dragCounterRef.current = Math.max(0, dragCounterRef.current - 1);
525+
if (dragCounterRef.current === 0) {
526+
setDragActive(false);
527+
}
528+
}
529+
530+
function handleDrop(e: DragEvent<HTMLDivElement>) {
531+
handleDragEvent(e);
532+
dragCounterRef.current = 0;
533+
handleFileSelect(e.dataTransfer.files?.[0] || null);
534+
}
535+
502536
const installing = uploadMutation.isPending || githubMutation.isPending;
503537

504538
const installTabs = [
@@ -564,18 +598,23 @@ function InstallPluginModal({
564598
{t('plugins.plugin_file')} <span className="text-danger">*</span>
565599
</label>
566600
<div
567-
className={`border-2 border-dashed rounded-md p-6 text-center cursor-pointer transition-colors ${
568-
selectedFile
601+
className={`border-2 border-dashed rounded-md p-6 text-center cursor-pointer transition-colors ${selectedFile
569602
? 'border-primary bg-primary-subtle'
570-
: 'border-glass-border hover:border-border-focus'
571-
}`}
603+
: dragActive
604+
? 'border-border-focus bg-[var(--ag-bg-muted)]'
605+
: 'border-glass-border hover:border-border-focus'
606+
}`}
572607
onClick={() => fileInputRef.current?.click()}
608+
onDragOver={handleDragEvent}
609+
onDragEnter={handleDragEnter}
610+
onDragLeave={handleDragLeave}
611+
onDrop={handleDrop}
573612
>
574613
<input
575614
ref={fileInputRef}
576615
type="file"
577616
className="hidden"
578-
onChange={(e) => setSelectedFile(e.target.files?.[0] || null)}
617+
onChange={(e) => handleFileSelect(e.target.files?.[0] || null)}
579618
/>
580619
{selectedFile ? (
581620
<div className="flex items-center justify-center gap-2">
@@ -587,7 +626,7 @@ function InstallPluginModal({
587626
</div>
588627
) : (
589628
<div>
590-
<Upload className="w-8 h-8 mx-auto mb-2 text-text-tertiary" />
629+
<Upload className={`w-8 h-8 mx-auto mb-2 ${dragActive ? 'text-primary' : 'text-text-tertiary'}`} />
591630
<p className="text-sm text-text-tertiary">
592631
{t('plugins.upload_hint')}
593632
</p>

0 commit comments

Comments
 (0)