Skip to content

Commit cc67249

Browse files
committed
feat: improve file tree
1 parent 44d720a commit cc67249

File tree

10 files changed

+185
-85
lines changed

10 files changed

+185
-85
lines changed

web/src/FileTree.js

Lines changed: 167 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,46 @@ class FileTree extends React.Component {
6666

6767
this.filePane = React.createRef();
6868
this.uploadedFileIdMap = {};
69+
this.handleKeyDown = this.handleKeyDown.bind(this);
70+
}
71+
72+
componentDidMount() {
73+
document.addEventListener("keydown", this.handleKeyDown);
74+
}
75+
76+
componentWillUnmount() {
77+
document.removeEventListener("keydown", this.handleKeyDown);
78+
}
79+
80+
handleKeyDown(e) {
81+
if (e.key === "Delete" && this.state.checkedFiles.length > 0) {
82+
e.preventDefault();
83+
this.batchDeleteFiles();
84+
}
6985
}
7086

7187
UNSAFE_componentWillMount() {
7288
this.getPermissions();
7389
}
7490

91+
batchDeleteFiles() {
92+
Modal.confirm({
93+
title: i18next.t("general:Confirm deletion"),
94+
content: i18next.t("store:Are you sure you want to delete the selected items?"),
95+
okText: i18next.t("general:OK"),
96+
cancelText: i18next.t("general:Cancel"),
97+
onOk: () => {
98+
this.state.checkedFiles.forEach(file => {
99+
this.deleteFile(file, file.isLeaf);
100+
});
101+
this.setState({
102+
checkedKeys: [],
103+
checkedFiles: [],
104+
});
105+
},
106+
});
107+
}
108+
75109
getPermissionMap(permissions) {
76110
const permissionMap = {};
77111
permissions.forEach((permission, index) => {
@@ -160,6 +194,19 @@ class FileTree extends React.Component {
160194
);
161195
}
162196

197+
uploadFiles(file, files) {
198+
const info = {
199+
fileList: Array.from(files).map(file => {
200+
file.uid = Date.now() + Math.floor(Math.random() * 1000);
201+
return {
202+
name: file.name,
203+
originFileObj: file,
204+
};
205+
}),
206+
};
207+
this.uploadFile(file, info);
208+
}
209+
163210
uploadFile(file, info) {
164211
const storeId = `${this.props.store.owner}/${this.props.store.name}`;
165212

@@ -457,6 +504,34 @@ class FileTree extends React.Component {
457504
tagStyle = {color: "rgba(100,100,100,0.6)", backgroundColor: "rgba(225,225,225,0.4)"};
458505
}
459506

507+
const targetKey = file.isLeaf ? file.parentKey : file.key;
508+
const isDraggingOver = this.state.dragOverKey === targetKey;
509+
510+
const handleDragOver = (e) => {
511+
e.preventDefault();
512+
this.setState({dragOverKey: targetKey});
513+
};
514+
515+
const handleDragLeave = (e) => {
516+
e.preventDefault();
517+
this.setState({dragOverKey: null});
518+
};
519+
520+
const handleDrop = (e) => {
521+
e.preventDefault();
522+
this.setState({dragOverKey: null});
523+
524+
const files = Array.from(e.dataTransfer.files || []);
525+
if (files.length === 0) {return;}
526+
if (!file.isLeaf) {
527+
this.uploadFiles?.(file, files);
528+
}
529+
};
530+
const wrapperStyle = {
531+
background: isDraggingOver ? "rgba(24,144,255,0.15)" : "transparent",
532+
transition: "background 0.2s",
533+
};
534+
460535
if (file.isLeaf) {
461536
return (
462537
<Tooltip color={"rgb(255,255,255,0.8)"} placement="right" title={
@@ -525,95 +600,102 @@ class FileTree extends React.Component {
525600
);
526601
} else {
527602
return (
528-
<Tooltip color={"rgb(255,255,255,0.8)"} placement="right" title={
529-
<div>
530-
{
531-
!isWritable ? null : (
532-
<React.Fragment>
533-
<Tooltip color={"rgb(255,255,255)"} placement="top" title={
534-
<span onClick={(e) => e.stopPropagation()}>
535-
<div style={{color: "black"}}>
536-
{i18next.t("store:New folder")}:
537-
</div>
538-
<Input.Group style={{marginTop: "5px"}} compact>
539-
<Input style={{width: "100px"}} value={this.state.newFolder} onChange={e => {
540-
this.setState({
541-
newFolder: e.target.value,
542-
});
543-
}} />
544-
<Button type="primary" onClick={(e) => {
545-
this.addFile(file, this.state.newFolder);
603+
<div
604+
style={wrapperStyle}
605+
onDragOver={handleDragOver}
606+
onDragLeave={handleDragLeave}
607+
onDrop={handleDrop}
608+
>
609+
<Tooltip color={"rgb(255,255,255,0.8)"} placement="right" title={
610+
<div>
611+
{
612+
!isWritable ? null : (
613+
<React.Fragment>
614+
<Tooltip color={"rgb(255,255,255)"} placement="top" title={
615+
<span onClick={(e) => e.stopPropagation()}>
616+
<div style={{color: "black"}}>
617+
{i18next.t("store:New folder")}:
618+
</div>
619+
<Input.Group style={{marginTop: "5px"}} compact>
620+
<Input style={{width: "100px"}} value={this.state.newFolder} onChange={e => {
621+
this.setState({
622+
newFolder: e.target.value,
623+
});
624+
}} />
625+
<Button type="primary" onClick={(e) => {
626+
this.addFile(file, this.state.newFolder);
627+
e.stopPropagation();
628+
}}
629+
>
630+
OK
631+
</Button>
632+
</Input.Group>
633+
</span>
634+
}>
635+
<span onClick={(e) => e.stopPropagation()}>
636+
<Button style={{marginRight: "5px"}} icon={<FolderAddOutlined />} size="small" onClick={(e) => {
546637
e.stopPropagation();
638+
}} />
639+
</span>
640+
</Tooltip>
641+
<Tooltip title={i18next.t("store:Upload file")}>
642+
<span onClick={(e) => e.stopPropagation()}>
643+
<Upload directory={true} multiple={true} accept="*" showUploadList={false} beforeUpload={file => {return false;}} onChange={(info) => {
644+
if (this.checkUploadFile(info)) {
645+
this.setState({
646+
isUploadFileModalVisible: true,
647+
file: file,
648+
info: info,
649+
});
650+
} else {
651+
this.uploadFile(file, info);
652+
}
547653
}}
548654
>
549-
OK
550-
</Button>
551-
</Input.Group>
552-
</span>
553-
}>
554-
<span onClick={(e) => e.stopPropagation()}>
555-
<Button style={{marginRight: "5px"}} icon={<FolderAddOutlined />} size="small" onClick={(e) => {
556-
e.stopPropagation();
557-
}} />
558-
</span>
559-
</Tooltip>
560-
<Tooltip title={i18next.t("store:Upload file")}>
561-
<span onClick={(e) => e.stopPropagation()}>
562-
<Upload multiple={true} accept="*" showUploadList={false} beforeUpload={file => {return false;}} onChange={(info) => {
563-
if (this.checkUploadFile(info)) {
564-
this.setState({
565-
isUploadFileModalVisible: true,
566-
file: file,
567-
info: info,
568-
});
569-
} else {
570-
this.uploadFile(file, info);
571-
}
572-
}}
573-
>
574-
<Button style={{marginRight: "5px"}} icon={<CloudUploadOutlined />} size="small" />
575-
</Upload>
576-
</span>
577-
</Tooltip>
578-
{
579-
file.key === "/" ? null : (
580-
<Tooltip title={i18next.t("general:Delete")}>
581-
<span onClick={(e) => e.stopPropagation()}>
582-
<Popconfirm
583-
title={`${i18next.t("general:Sure to delete")}: ${file.title} ?`}
584-
onConfirm={(e) => {
585-
this.deleteFile(file, false);
586-
}}
587-
okText={i18next.t("general:OK")}
588-
cancelText={i18next.t("general:Cancel")}
589-
>
590-
<Button style={{marginRight: "5px"}} icon={<DeleteOutlined />} size="small" />
591-
</Popconfirm>
592-
</span>
593-
</Tooltip>
594-
)
595-
}
596-
</React.Fragment>
597-
)
655+
<Button style={{marginRight: "5px"}} icon={<CloudUploadOutlined />} size="small" />
656+
</Upload>
657+
</span>
658+
</Tooltip>
659+
{
660+
file.key === "/" ? null : (
661+
<Tooltip title={i18next.t("general:Delete")}>
662+
<span onClick={(e) => e.stopPropagation()}>
663+
<Popconfirm
664+
title={`${i18next.t("general:Sure to delete")}: ${file.title} ?`}
665+
onConfirm={(e) => {
666+
this.deleteFile(file, false);
667+
}}
668+
okText={i18next.t("general:OK")}
669+
cancelText={i18next.t("general:Cancel")}
670+
>
671+
<Button style={{marginRight: "5px"}} icon={<DeleteOutlined />} size="small" />
672+
</Popconfirm>
673+
</span>
674+
</Tooltip>
675+
)
676+
}
677+
</React.Fragment>
678+
)
679+
}
680+
<Tooltip title={isAdmin ? i18next.t("store:Add Permission") :
681+
i18next.t("store:Apply for Permission")}>
682+
<Button icon={<FileDoneOutlined />} size="small" onClick={(e) => {
683+
PermissionUtil.addPermission(this.props.account, this.props.store, file);
684+
e.stopPropagation();
685+
}} />
686+
</Tooltip>
687+
</div>
688+
}>
689+
<span style={tagStyle}>
690+
{file.title}
691+
</span>
692+
&nbsp;
693+
&nbsp;
694+
{
695+
(this.state.permissionMap === null) ? null : this.renderPermissions(this.state.permissionMap[file.key], isReadable)
598696
}
599-
<Tooltip title={isAdmin ? i18next.t("store:Add Permission") :
600-
i18next.t("store:Apply for Permission")}>
601-
<Button icon={<FileDoneOutlined />} size="small" onClick={(e) => {
602-
PermissionUtil.addPermission(this.props.account, this.props.store, file);
603-
e.stopPropagation();
604-
}} />
605-
</Tooltip>
606-
</div>
607-
}>
608-
<span style={tagStyle}>
609-
{file.title}
610-
</span>
611-
&nbsp;
612-
&nbsp;
613-
{
614-
(this.state.permissionMap === null) ? null : this.renderPermissions(this.state.permissionMap[file.key], isReadable)
615-
}
616-
</Tooltip>
697+
</Tooltip>
698+
</div>
617699
);
618700
}
619701
}}

web/src/locales/de/data.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
"Client IP - Tooltip": "Original-IP-Adresse des Clients",
153153
"Close": "Schließen",
154154
"Cloud Resources": "Cloud-Ressourcen",
155+
"Confirm deletion": "Confirm deletion",
155156
"Connections": "Verbindungssitzungen",
156157
"Containers": "Container",
157158
"Copied to clipboard successfully": "Erfolgreich in die Zwischenablage kopiert",
@@ -646,6 +647,7 @@
646647
"Agent provider - Tooltip": "Agent-Dienstleister",
647648
"All": "Alle",
648649
"Apply for Permission": "Berechtigung beantragen",
650+
"Are you sure you want to delete the selected items?": "Are you sure you want to delete the selected items?",
649651
"Auto read": "Automatisches Vorlesen",
650652
"Biology": "Biologie",
651653
"Chat count": "Chat-Anzahl",

web/src/locales/en/data.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
"Client IP - Tooltip": "Client's original IP address",
153153
"Close": "Close",
154154
"Cloud Resources": "Cloud Resources",
155+
"Confirm deletion": "Confirm deletion",
155156
"Connections": "Connections",
156157
"Containers": "Containers",
157158
"Copied to clipboard successfully": "Copied to clipboard successfully",
@@ -636,6 +637,7 @@
636637
"Agent provider - Tooltip": "Agent service provider",
637638
"All": "All",
638639
"Apply for Permission": "Apply for Permission",
640+
"Are you sure you want to delete the selected items?": "Are you sure you want to delete the selected items?",
639641
"Auto read": "Auto read",
640642
"Biology": "Biology",
641643
"Chat count": "Chat count",

web/src/locales/es/data.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
"Client IP - Tooltip": "Dirección IP original del cliente",
153153
"Close": "Cerrar",
154154
"Cloud Resources": "Recursos en la nube",
155+
"Confirm deletion": "Confirm deletion",
155156
"Connections": "Sesiones de conexión",
156157
"Containers": "Contenedores",
157158
"Copied to clipboard successfully": "Copiado al portapapeles con éxito",
@@ -646,6 +647,7 @@
646647
"Agent provider - Tooltip": "Proveedor de servicio de agente",
647648
"All": "Todos",
648649
"Apply for Permission": "Solicitar permiso",
650+
"Are you sure you want to delete the selected items?": "Are you sure you want to delete the selected items?",
649651
"Auto read": "Lectura automática",
650652
"Biology": "Biología",
651653
"Chat count": "Número de chats",

web/src/locales/fr/data.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
"Client IP - Tooltip": "Adresse IP originale du client",
153153
"Close": "Fermer",
154154
"Cloud Resources": "Ressources cloud",
155+
"Confirm deletion": "Confirm deletion",
155156
"Connections": "Connecter une session",
156157
"Containers": "Conteneurs",
157158
"Copied to clipboard successfully": "Copié dans le presse-papiers avec succès",
@@ -646,6 +647,7 @@
646647
"Agent provider - Tooltip": "Fournisseur de service d'agent",
647648
"All": "Tous",
648649
"Apply for Permission": "Demander une permission",
650+
"Are you sure you want to delete the selected items?": "Are you sure you want to delete the selected items?",
649651
"Auto read": "Lecture automatique",
650652
"Biology": "Biologie",
651653
"Chat count": "Nombre de chats",

web/src/locales/id/data.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
"Client IP - Tooltip": "Alamat IP asli klien",
153153
"Close": "Tutup",
154154
"Cloud Resources": "Sumber daya cloud",
155+
"Confirm deletion": "Confirm deletion",
155156
"Connections": "Sesi koneksi",
156157
"Containers": "Kontainer",
157158
"Copied to clipboard successfully": "Berhasil disalin ke papan klip",
@@ -646,6 +647,7 @@
646647
"Agent provider - Tooltip": "Penyedia layanan agent",
647648
"All": "Semua",
648649
"Apply for Permission": "Aplikasikan izin",
650+
"Are you sure you want to delete the selected items?": "Are you sure you want to delete the selected items?",
649651
"Auto read": "Bacaan otomatis",
650652
"Biology": "Biologi",
651653
"Chat count": "Jumlah chat",

web/src/locales/ja/data.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
"Client IP - Tooltip": "クライアントの元のIPアドレス",
153153
"Close": "閉じる",
154154
"Cloud Resources": "クラウドリソース",
155+
"Confirm deletion": "Confirm deletion",
155156
"Connections": "接続セッション",
156157
"Containers": "コンテナ",
157158
"Copied to clipboard successfully": "クリップボードに正常にコピーされました",
@@ -646,6 +647,7 @@
646647
"Agent provider - Tooltip": "Agentサービスプロバイダ",
647648
"All": "全部",
648649
"Apply for Permission": "権限を申請",
650+
"Are you sure you want to delete the selected items?": "Are you sure you want to delete the selected items?",
649651
"Auto read": "自動読み上げ",
650652
"Biology": "生物学",
651653
"Chat count": "チャット数",

0 commit comments

Comments
 (0)