Showing
3 changed files
with
86 additions
and
4 deletions
| ... | @@ -3,9 +3,11 @@ import { Popconfirm, Popover, Button, message } from "antd"; | ... | @@ -3,9 +3,11 @@ import { Popconfirm, Popover, Button, message } from "antd"; |
| 3 | import { FileItem } from "./useFileList"; | 3 | import { FileItem } from "./useFileList"; |
| 4 | import styles from "./FileItemActions.module.scss"; | 4 | import styles from "./FileItemActions.module.scss"; |
| 5 | import { FileListPopover } from "./FileListPopover"; | 5 | import { FileListPopover } from "./FileListPopover"; |
| 6 | +import { FileRenamePopover } from "./FileRenamePopover"; | ||
| 6 | 7 | ||
| 7 | export type FileItemActionsProps = { | 8 | export type FileItemActionsProps = { |
| 8 | item: FileItem; | 9 | item: FileItem; |
| 10 | + onRename: (id: number, name: string) => void; | ||
| 9 | onMove: (id: number, to: number) => void; | 11 | onMove: (id: number, to: number) => void; |
| 10 | onCopy: (id: number, to: number) => void; | 12 | onCopy: (id: number, to: number) => void; |
| 11 | onDelete: (id: number) => void; | 13 | onDelete: (id: number) => void; |
| ... | @@ -13,18 +15,43 @@ export type FileItemActionsProps = { | ... | @@ -13,18 +15,43 @@ export type FileItemActionsProps = { |
| 13 | 15 | ||
| 14 | export function FileItemActions({ | 16 | export function FileItemActions({ |
| 15 | item, | 17 | item, |
| 18 | + onRename, | ||
| 16 | onMove, | 19 | onMove, |
| 17 | onCopy, | 20 | onCopy, |
| 18 | onDelete, | 21 | onDelete, |
| 19 | }: FileItemActionsProps) { | 22 | }: FileItemActionsProps) { |
| 23 | + const [rename, setRename] = useState<boolean>(false); | ||
| 20 | const [move, setMove] = useState<boolean>(false); | 24 | const [move, setMove] = useState<boolean>(false); |
| 21 | const [copy, setCopy] = useState<boolean>(false); | 25 | const [copy, setCopy] = useState<boolean>(false); |
| 22 | 26 | ||
| 23 | return ( | 27 | return ( |
| 24 | <div className={styles.actions}> | 28 | <div className={styles.actions}> |
| 25 | - <Button type="link" size="small"> | 29 | + <Popover |
| 26 | - 이름 변경 | 30 | + title="변경할 이름을 입력하세요" |
| 27 | - </Button> | 31 | + content={ |
| 32 | + <FileRenamePopover | ||
| 33 | + name={item.name} | ||
| 34 | + onRename={(name: string) => { | ||
| 35 | + if (name === item.name) { | ||
| 36 | + return message.error("동일한 이름으로는 변경할 수 없습니다"); | ||
| 37 | + } | ||
| 38 | + if (!name) { | ||
| 39 | + return message.error("변경할 이름을 입력하세요"); | ||
| 40 | + } | ||
| 41 | + onRename(item.id, name); | ||
| 42 | + setRename(false); | ||
| 43 | + }} | ||
| 44 | + onCancel={() => setRename(false)} | ||
| 45 | + /> | ||
| 46 | + } | ||
| 47 | + trigger="click" | ||
| 48 | + visible={rename} | ||
| 49 | + onVisibleChange={setRename} | ||
| 50 | + > | ||
| 51 | + <Button type="link" size="small"> | ||
| 52 | + 이름 변경 | ||
| 53 | + </Button> | ||
| 54 | + </Popover> | ||
| 28 | <Button type="link" size="small"> | 55 | <Button type="link" size="small"> |
| 29 | 공유 | 56 | 공유 |
| 30 | </Button> | 57 | </Button> | ... | ... |
| ... | @@ -17,6 +17,23 @@ export function FileList() { | ... | @@ -17,6 +17,23 @@ export function FileList() { |
| 17 | 17 | ||
| 18 | const api = useApi(); | 18 | const api = useApi(); |
| 19 | 19 | ||
| 20 | + const handleRename = useCallback( | ||
| 21 | + async (id: number, name: string) => { | ||
| 22 | + try { | ||
| 23 | + const body = new URLSearchParams(); | ||
| 24 | + body.set("name", name); | ||
| 25 | + | ||
| 26 | + await api.post(`/items/${id}/move/`, { body }); | ||
| 27 | + await reload(); | ||
| 28 | + | ||
| 29 | + message.info("이름이 변경되었습니다"); | ||
| 30 | + } catch { | ||
| 31 | + message.error("이름 변경에 실패했습니다"); | ||
| 32 | + } | ||
| 33 | + }, | ||
| 34 | + [api, reload] | ||
| 35 | + ); | ||
| 36 | + | ||
| 20 | const handleMove = useCallback( | 37 | const handleMove = useCallback( |
| 21 | async (id: number, to: number) => { | 38 | async (id: number, to: number) => { |
| 22 | try { | 39 | try { |
| ... | @@ -97,6 +114,7 @@ export function FileList() { | ... | @@ -97,6 +114,7 @@ export function FileList() { |
| 97 | <Table | 114 | <Table |
| 98 | rowKey="id" | 115 | rowKey="id" |
| 99 | columns={getColumns({ | 116 | columns={getColumns({ |
| 117 | + handleRename, | ||
| 100 | handleMove, | 118 | handleMove, |
| 101 | handleCopy, | 119 | handleCopy, |
| 102 | handleDelete, | 120 | handleDelete, |
| ... | @@ -109,19 +127,21 @@ export function FileList() { | ... | @@ -109,19 +127,21 @@ export function FileList() { |
| 109 | } | 127 | } |
| 110 | 128 | ||
| 111 | type GetColumnsParams = { | 129 | type GetColumnsParams = { |
| 130 | + handleRename: (id: number, name: string) => void; | ||
| 112 | handleMove: (id: number, to: number) => void; | 131 | handleMove: (id: number, to: number) => void; |
| 113 | handleCopy: (id: number, to: number) => void; | 132 | handleCopy: (id: number, to: number) => void; |
| 114 | handleDelete: (id: number) => void; | 133 | handleDelete: (id: number) => void; |
| 115 | }; | 134 | }; |
| 116 | 135 | ||
| 117 | function getColumns({ | 136 | function getColumns({ |
| 137 | + handleRename, | ||
| 118 | handleMove, | 138 | handleMove, |
| 119 | handleCopy, | 139 | handleCopy, |
| 120 | handleDelete, | 140 | handleDelete, |
| 121 | }: GetColumnsParams): ColumnsType<FileItem> { | 141 | }: GetColumnsParams): ColumnsType<FileItem> { |
| 122 | return [ | 142 | return [ |
| 123 | { | 143 | { |
| 124 | - title: "파일명", | 144 | + title: "이름", |
| 125 | key: "name", | 145 | key: "name", |
| 126 | dataIndex: "name", | 146 | dataIndex: "name", |
| 127 | render: (_name: string, item) => <FileListItem item={item} />, | 147 | render: (_name: string, item) => <FileListItem item={item} />, |
| ... | @@ -143,6 +163,7 @@ function getColumns({ | ... | @@ -143,6 +163,7 @@ function getColumns({ |
| 143 | item.is_folder ? null : ( | 163 | item.is_folder ? null : ( |
| 144 | <FileItemActions | 164 | <FileItemActions |
| 145 | item={item} | 165 | item={item} |
| 166 | + onRename={handleRename} | ||
| 146 | onMove={handleMove} | 167 | onMove={handleMove} |
| 147 | onCopy={handleCopy} | 168 | onCopy={handleCopy} |
| 148 | onDelete={handleDelete} | 169 | onDelete={handleDelete} | ... | ... |
frontend/src/file/FileRenamePopover.tsx
0 → 100644
| 1 | +import React, { useState } from "react"; | ||
| 2 | +import { Button, Input } from "antd"; | ||
| 3 | + | ||
| 4 | +export type FileRenamePopoverProps = { | ||
| 5 | + name: string; | ||
| 6 | + onRename: (name: string) => void; | ||
| 7 | + onCancel?: () => void; | ||
| 8 | +}; | ||
| 9 | + | ||
| 10 | +export function FileRenamePopover({ | ||
| 11 | + name: oldName, | ||
| 12 | + onRename, | ||
| 13 | + onCancel, | ||
| 14 | +}: FileRenamePopoverProps) { | ||
| 15 | + const [name, setName] = useState<string>(oldName); | ||
| 16 | + return ( | ||
| 17 | + <div> | ||
| 18 | + <Input | ||
| 19 | + value={name} | ||
| 20 | + onChange={(event) => setName(event.target.value)} | ||
| 21 | + placeholder="이름" | ||
| 22 | + style={{ marginBottom: 10 }} | ||
| 23 | + /> | ||
| 24 | + <div className="ant-popover-buttons"> | ||
| 25 | + <Button size="small" onClick={onCancel}> | ||
| 26 | + 취소 | ||
| 27 | + </Button> | ||
| 28 | + <Button type="primary" size="small" onClick={() => onRename(name)}> | ||
| 29 | + 변경 | ||
| 30 | + </Button> | ||
| 31 | + </div> | ||
| 32 | + </div> | ||
| 33 | + ); | ||
| 34 | +} |
-
Please register or login to post a comment