diff --git a/frontend/src/metadata/components/popover/view-popover/index.css b/frontend/src/metadata/components/popover/view-popover/index.css index 8316c503ed..fa320fee90 100644 --- a/frontend/src/metadata/components/popover/view-popover/index.css +++ b/frontend/src/metadata/components/popover/view-popover/index.css @@ -1,4 +1,3 @@ -.sf-metadata-rename-view-popover .popover, .sf-metadata-addview-popover .popover { min-width: 280px; padding: 0.5rem 0; @@ -23,18 +22,6 @@ font-size: 0.875rem; } -.sf-metadata-rename-view-popover .sf-metadata-rename-view-popover-header { - width: 100%; - height: 1.5rem; - padding: 0 1rem; - display: flex; - align-items: center; - justify-content: left; - color: #666; - opacity: 1; - font-size: 0.875rem; -} - .sf-metadata-addview-popover .sf-metadata-addview-popover-body { width: 100%; @@ -58,10 +45,6 @@ fill: #666; } -.sf-metadata-rename-view-popover-body { - padding: 0 0.5rem; -} - .dropdown-item:hover .metadata-view-icon { color: #fff; fill: #fff; diff --git a/frontend/src/metadata/components/popover/view-popover/index.js b/frontend/src/metadata/components/popover/view-popover/index.js index 39ad3c2597..b8760c19c6 100644 --- a/frontend/src/metadata/components/popover/view-popover/index.js +++ b/frontend/src/metadata/components/popover/view-popover/index.js @@ -1,2 +1 @@ -export { default as Rename } from './rename'; export { default as AddView } from './add-view'; diff --git a/frontend/src/metadata/components/popover/view-popover/rename/index.js b/frontend/src/metadata/components/popover/view-popover/rename/index.js deleted file mode 100644 index 99d080393d..0000000000 --- a/frontend/src/metadata/components/popover/view-popover/rename/index.js +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useCallback, useRef, useState } from 'react'; -import PropTypes from 'prop-types'; -import { Alert, Input } from 'reactstrap'; -import { CustomizePopover } from '@seafile/sf-metadata-ui-component'; -import { gettext } from '../../../../../utils/constants'; -import { isValidViewName } from '../../../../utils/validate'; -import { isEnter } from '../../../../utils/hotkey'; - -import '../index.css'; - -const Rename = ({ value, target, otherViewsName, toggle, onSubmit }) => { - const [inputValue, setInputValue] = useState(value || ''); - const [errorMessage, setErrorMessage] = useState(''); - const inputRef = useRef(null); - - const onChange = useCallback((e) => { - setInputValue(e.target.value); - }, []); - - const onToggle = useCallback(() => { - toggle(); - }, [toggle]); - - const handleSubmit = useCallback((event) => { - event.preventDefault(); - event.stopPropagation(); - const { isValid, message } = isValidViewName(inputValue, otherViewsName); - if (!isValid) { - setErrorMessage(message); - inputRef.current.focus(); - return; - } - if (message === value) { - onToggle(); - return; - } - onSubmit(message); - }, [value, inputValue, otherViewsName, onSubmit, onToggle]); - - const onKeyDown = useCallback((event) => { - if (isEnter(event)) { - handleSubmit(event); - } - }, [handleSubmit]); - - return ( - -
- {gettext('Rename view')} -
-
-
- - {errorMessage && ({errorMessage})} -
-
- ); -}; - -Rename.propTypes = { - value: PropTypes.string, - target: PropTypes.string.isRequired, - otherViewsName: PropTypes.array, - toggle: PropTypes.func.isRequired, - onSubmit: PropTypes.func.isRequired, -}; - -export default Rename; diff --git a/frontend/src/metadata/metadata-tree-view/index.css b/frontend/src/metadata/metadata-tree-view/index.css index 8fa06bed82..6eeabedcc4 100644 --- a/frontend/src/metadata/metadata-tree-view/index.css +++ b/frontend/src/metadata/metadata-tree-view/index.css @@ -59,11 +59,12 @@ } .metadata-tree-view .sf-metadata-view-input { - width: 100%; + width: 95%; height: 24px; position: relative; font-size: 14px; margin-top: 2px; + box-shadow: none; } .metadata-tree-view .metadata-views-icon { diff --git a/frontend/src/metadata/metadata-tree-view/index.js b/frontend/src/metadata/metadata-tree-view/index.js index 2e4a2df1f8..aea3221b79 100644 --- a/frontend/src/metadata/metadata-tree-view/index.js +++ b/frontend/src/metadata/metadata-tree-view/index.js @@ -149,6 +149,13 @@ const MetadataTreeView = ({ userPerm, currentPath }) => { } }, [handleInputSubmit]); + useEffect(() => { + if (showInput && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [showInput]); + return ( <>
diff --git a/frontend/src/metadata/metadata-tree-view/view-item/index.js b/frontend/src/metadata/metadata-tree-view/view-item/index.js index bad7b638ad..e2a53914cc 100644 --- a/frontend/src/metadata/metadata-tree-view/view-item/index.js +++ b/frontend/src/metadata/metadata-tree-view/view-item/index.js @@ -1,13 +1,16 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Input } from 'reactstrap'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { gettext } from '../../../utils/constants'; import Icon from '../../../components/icon'; import ItemDropdownMenu from '../../../components/dropdown-menu/item-dropdown-menu'; -import { Rename } from '../../components/popover/view-popover'; import { Utils, isMobile } from '../../../utils/utils'; import { useMetadata } from '../../hooks'; import { VIEW_TYPE_ICON } from '../../constants'; +import { isValidViewName } from '../../utils/validate'; +import { isEnter } from '../../utils/hotkey'; +import toaster from '../../../components/toast'; import './index.css'; @@ -25,7 +28,11 @@ const ViewItem = ({ const [highlight, setHighlight] = useState(false); const [freeze, setFreeze] = useState(false); const [isDropShow, setDropShow] = useState(false); - const [isShowRenamePopover, setRenamePopoverShow] = useState(false); + const [isRenaming, setRenaming] = useState(false); + const [inputValue, setInputValue] = useState(view.name || ''); + + const inputRef = useRef(null); + const { viewsMap } = useMetadata(); const otherViewsName = Object.values(viewsMap).filter(v => v._id !== view._id).map(v => v.name); @@ -78,7 +85,7 @@ const ViewItem = ({ const operationClick = useCallback((operationKey) => { if (operationKey === 'rename') { - setRenamePopoverShow(true); + setRenaming(true); return; } @@ -93,13 +100,9 @@ const ViewItem = ({ } }, [onDelete, onCopy]); - const closeRenamePopover = useCallback(() => { - setRenamePopoverShow(false); - }, []); - const renameView = useCallback((name, failCallback) => { onUpdate({ name }, () => { - setRenamePopoverShow(false); + setRenaming(false); document.title = `${name} - Seafile`; }, (error) => { failCallback(error); @@ -143,6 +146,57 @@ const ViewItem = ({ onMove && onMove(dragData.view_id, view._id); }, [canDrop, view, onMove]); + const onChange = useCallback((e) => { + setInputValue(e.target.value); + }, []); + + const handleSubmit = useCallback((event) => { + event.preventDefault(); + event.stopPropagation(); + const { isValid, message } = isValidViewName(inputValue, otherViewsName); + if (!isValid) { + toaster.danger(message); + return; + } + if (message === view.name) { + setRenaming(false); + return; + } + renameView(message); + }, [view, inputValue, otherViewsName, renameView]); + + const onKeyDown = useCallback((event) => { + if (isEnter(event)) { + handleSubmit(event); + unfreezeItem(); + } + }, [handleSubmit, unfreezeItem]); + + useEffect(() => { + if (isRenaming && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [isRenaming]); + + useEffect(() => { + const handleClickOutside = (event) => { + if (inputRef.current && !inputRef.current.contains(event.target)) { + handleSubmit(event); + } + }; + + if (isRenaming) { + document.addEventListener('mousedown', handleClickOutside); + } else { + document.removeEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [isRenaming, handleSubmit]); + return ( <>
- {view.name} + {isRenaming ? ( + setRenaming(false)} + onKeyDown={onKeyDown} + /> + ) : view.name}
@@ -183,9 +247,6 @@ const ViewItem = ({ )}
- {isShowRenamePopover && ( - - )} ); };