diff --git a/frontend/src/components/file-view/comment-panel.js b/frontend/src/components/file-view/comment-panel.js index 9bf0aec521..d5d496f857 100644 --- a/frontend/src/components/file-view/comment-panel.js +++ b/frontend/src/components/file-view/comment-panel.js @@ -6,10 +6,14 @@ import { Utils } from '../../utils/utils'; import toaster from '../toast'; import CommentList from './comment-widget/comment-list'; import ReplyList from './comment-widget/reply-list'; +import LocalStorage from '../../utils/local-storage-utils'; +import ResizeWidth from './resize-width'; import '../../css/comments-list.css'; -const { username, repoID, filePath, fileUuid } = window.app.pageOptions; +const { username, repoID, filePath, fileUuid, fileName } = window.app.pageOptions; +const MIN_PANEL_WIDTH = 360; +const MAX_PANEL_WIDTH = 620; const CommentPanelPropTypes = { toggleCommentPanel: PropTypes.func.isRequired, @@ -28,10 +32,27 @@ class CommentPanel extends React.Component { participants: null, relatedUsers: null, currentComment: null, + width: MIN_PANEL_WIDTH, }; this.toBeAddedParticipant = []; } + panelWrapperStyle = () => { + + let style = { + width: this.state.width, + zIndex: 101, + }; + + if (!style.width || style.width < MIN_PANEL_WIDTH) { + style.width = MIN_PANEL_WIDTH; + } else if (style.width > MAX_PANEL_WIDTH) { + style.width = MAX_PANEL_WIDTH; + } + + return style; + }; + forceUpdate = () => { this.listComments(); this.getParticipants(); @@ -163,6 +184,15 @@ class CommentPanel extends React.Component { this.listComments(); this.getParticipants(); this.listRepoRelatedUsers(); + + const newfileType = fileName?.split('.').pop(); + const settings = LocalStorage.getItem(`${newfileType}_comment_storage`) || {}; + const { panelWidth } = settings; + const width = Math.max( + MIN_PANEL_WIDTH, + Math.min(parseInt(panelWidth, 10) || MIN_PANEL_WIDTH, MAX_PANEL_WIDTH) + ); + this.setState({ width }); } onClickComment = (currentComment) => { @@ -173,10 +203,21 @@ class CommentPanel extends React.Component { this.setState({ currentComment: null }); }; + resizeWidth = (width) => { + this.setState({ width: width }); + }; + + resizeWidthEnd = (width) => { + const newfileType = fileName?.split('.').pop(); + const settings = LocalStorage.getItem(`${newfileType}_comment_storage`) || {}; + LocalStorage.setItem(`${newfileType}_comment_storage`, JSON.stringify({ ...settings, panelWidth: width })); + }; + render() { const { commentsList } = this.state; return ( -
+
+ { this.state.currentComment ? { @@ -144,6 +158,31 @@ class FileView extends React.Component { this.commentPanelRef = ref; }; + panelWrapperStyle = () => { + let style = { + width: this.state.width, + zIndex: 101, + }; + + if (!style.width || style.width < MIN_PANEL_WIDTH) { + style.width = MIN_PANEL_WIDTH; + } else if (style.width > MAX_PANEL_WIDTH) { + style.width = MAX_PANEL_WIDTH; + } + + return style; + }; + + resizeWidth = (width) => { + this.setState({ width: width }); + }; + + resizeWidthEnd = (width) => { + const newfileType = fileName?.split('.').pop(); + const settings = LocalStorage.getItem(`${newfileType}_detail_storage`) || {}; + LocalStorage.setItem(`${newfileType}_detail_storage`, JSON.stringify({ ...settings, panelWidth: width })); + }; + render() { const { isOnlyofficeFile = false } = this.props; const { isDetailsPanelOpen, isHeaderShown } = this.state; @@ -209,13 +248,17 @@ class FileView extends React.Component { {isDetailsPanelOpen && ( - +
+ + +
)} diff --git a/frontend/src/components/file-view/resize-width.js b/frontend/src/components/file-view/resize-width.js new file mode 100644 index 0000000000..29ca27b56b --- /dev/null +++ b/frontend/src/components/file-view/resize-width.js @@ -0,0 +1,148 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import PropTypes from 'prop-types'; + +import '../../css/resize-width.css'; + +const ResizeWidth = ({ minWidth, maxWidth, resizeWidth: resizeWidthAPI, resizeWidthEnd }) => { + const [isShowHandlerBar, setIsShowHandlerBar] = useState(false); + const [drag, setDrag] = useState(null); + + const handlerRef = useRef(null); + const handlerBarRef = useRef(null); + + + const setHandlerBarTop = (handlerTop) => { + if (!handlerBarRef.current || handlerTop < 0) return; + handlerBarRef.current.style.top = handlerTop + 'px'; + }; + + const setHandlerBarPosition = (event) => { + if (!handlerRef.current) return; + const { top } = handlerRef.current.getBoundingClientRect(); + const handlerTop = event.pageY - top - 26 / 2; + setHandlerBarTop(handlerTop); + }; + + const getWidthFromMouseEvent = (event) => { + return event.pageX || (event.touches && event.touches[0] && event.touches[0].pageX) || + (event.changedTouches && event.changedTouches[event.changedTouches.length - 1].pageX); + }; + + const calculateResizedWidth = (event) => { + const width = getWidthFromMouseEvent(event); + const resizedWidth = document.body.clientWidth - width; + if ((minWidth && resizedWidth < minWidth) || (maxWidth && resizedWidth > maxWidth)) return -1; + return resizedWidth; + }; + + const onResizeWidth = (event) => { + const resizedWidth = calculateResizedWidth(event); + if (resizedWidth < 0) return; + if (resizeWidthAPI) { + resizeWidthAPI(resizedWidth); + } + }; + + const onDrag = (event) => { + onResizeWidth(event); + }; + + const onDragStart = useCallback((event) => { + if (event && event.dataTransfer && event.dataTransfer.setData) { + event.dataTransfer.setData('text/plain', 'dummy'); + } + }, []); + + const onDragEnd = (event) => { + onResizeWidth(event); + }; + + const onMouseLeave = () => { + setIsShowHandlerBar(false); + }; + + const onMouseEnter = (event) => { + setIsShowHandlerBar(true); + setHandlerBarPosition(event); + if (handlerRef.current) { + handlerRef.current.addEventListener('mouseleave', onMouseLeave); + } + }; + + const onMouseOver = (event) => { + setHandlerBarPosition(event); + }; + + const onMouseDown = (event) => { + event.preventDefault && event.preventDefault(); + const currDrag = onDragStart(event); + if (currDrag === null && event.button !== 0) return; + + window.addEventListener('mouseup', onMouseUp); + window.addEventListener('mousemove', onMouseMove); + + if (handlerRef.current) { + handlerRef.current.removeEventListener('mouseleave', onMouseLeave); + } + + setDrag(currDrag); + }; + + const onMouseMove = (event) => { + event.preventDefault && event.preventDefault(); + if (!drag === null) return; + onDrag(event); + }; + + const onMouseUp = (event) => { + window.removeEventListener('mouseup', onMouseUp); + window.removeEventListener('mousemove', onMouseMove); + onDragEnd(event, drag); + setHandlerBarTop(-9999); + setDrag(null); + setIsShowHandlerBar(false); + if (resizeWidthEnd) { + const resizeWidth = calculateResizedWidth(event); + if (resizeWidth < 0) return; + resizeWidthEnd(resizeWidth); + } + }; + + useEffect(() => { + return () => { + window.removeEventListener('mouseup', onMouseUp); + window.removeEventListener('mousemove', onMouseMove); + }; + + // eslint-disable-next-line + }, []); + + return ( +
+
+ {isShowHandlerBar && ( +
+ )} +
+
+ ); +}; + +ResizeWidth.propTypes = { + minWidth: PropTypes.number, + maxWidth: PropTypes.number, + resizeWidth: PropTypes.func, + resizeWidthEnd: PropTypes.func, +}; + +export default ResizeWidth; diff --git a/frontend/src/css/comments-list.css b/frontend/src/css/comments-list.css index c1b1479194..d138dc8a1a 100644 --- a/frontend/src/css/comments-list.css +++ b/frontend/src/css/comments-list.css @@ -2,6 +2,7 @@ background: var(--bs-body-bg); display: flex; flex-direction: column; + position: relative; } .seafile-comment-title { @@ -19,7 +20,7 @@ font-weight: 500; } -.seafile-comment-title .sdoc-icon-btn { +.seafile-comment-title .sdoc-icon-btn { display: inline-flex; align-items: center; justify-content: center; diff --git a/frontend/src/css/file-view.css b/frontend/src/css/file-view.css index 31b32a0fd0..0f4267003f 100644 --- a/frontend/src/css/file-view.css +++ b/frontend/src/css/file-view.css @@ -154,6 +154,11 @@ body { z-index: 50; } +.seafile-file-detail-right-panel-wrapper { + display: flex; + position: relative; +} + @keyframes move { from { right: -500px; diff --git a/frontend/src/css/resize-width.css b/frontend/src/css/resize-width.css new file mode 100644 index 0000000000..069829a6c1 --- /dev/null +++ b/frontend/src/css/resize-width.css @@ -0,0 +1,37 @@ +.seafile-resize-width-handler { + height: 100%; + position: absolute; + right: 0; + top: 0; + width: 6px; +} + +.seafile-resize-width-handler.resize-handler-placement-right { + left: 0; + right: auto; +} + +.seafile-resize-width-handler:hover { + cursor: col-resize; +} + +.seafile-resize-width-handler .seafile-resize-width-handler-content { + background-color: initial; + height: 100%; + position: relative; + width: 2px; +} + +.seafile-resize-width-handler:hover .seafile-resize-width-handler-content { + background-color: #ccc; +} + +.seafile-resize-width-handler .seafile-resize-width-handler-bar { + background-color: #2d7ff9; + border-radius: 3px; + content: ""; + left: 50%; + margin-left: -3px; + position: absolute; + width: 6px; +}