);
};
diff --git a/frontend/src/components/dialog/trash-dialog/table/index.js b/frontend/src/components/dialog/trash-dialog/table/index.js
index 61b538d6a3f..f1f3488d735 100644
--- a/frontend/src/components/dialog/trash-dialog/table/index.js
+++ b/frontend/src/components/dialog/trash-dialog/table/index.js
@@ -1,51 +1,31 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../../../utils/constants';
import FolderRecords from './folder-records';
import FileRecords from './file-records';
+import FixedWidthTable from '../../../common/fixed-width-table';
const Table = ({ repoID, renderFolder, data }) => {
- const [containerWidth, setContainerWidth] = useState(0);
-
- const containerRef = useRef(null);
-
- useEffect(() => {
- const container = containerRef.current;
- const handleResize = () => {
- if (!container) return;
- setContainerWidth(container.offsetWidth);
- };
- const resizeObserver = new ResizeObserver(handleResize);
- container && resizeObserver.observe(container);
-
- return () => {
- container && resizeObserver.unobserve(container);
- };
- }, []);
+ const headers = useMemo(() => [
+ { isFixed: true, width: 40, className: 'pl-2 pr-2' },
+ { isFixed: false, width: 0.25, children: gettext('Name') },
+ { isFixed: false, width: 0.4, children: gettext('Original path') },
+ { isFixed: false, width: 0.12, children: gettext('Delete Time') },
+ { isFixed: false, width: 0.13, children: gettext('Size') },
+ { isFixed: false, width: 0.1, children: gettext('Size') },
+ ], []);
const { items, showFolder, commitID, baseDir, folderPath, folderItems } = data;
return (
-
-
-
-
- {/* icon */} |
- {gettext('Name')} |
- {gettext('Original path')} |
- {gettext('Delete Time')} |
- {gettext('Size')} |
- {/* op */} |
-
-
-
- {showFolder ? (
-
- ) : (
-
- )}
-
-
+
+
+ {showFolder ? (
+
+ ) : (
+
+ )}
+
);
};
diff --git a/frontend/src/components/dirent-detail/index.js b/frontend/src/components/dirent-detail/index.js
index 30f37d8b156..542f244cd54 100644
--- a/frontend/src/components/dirent-detail/index.js
+++ b/frontend/src/components/dirent-detail/index.js
@@ -10,9 +10,11 @@ import { METADATA_MODE, TAGS_MODE } from '../dir-view-mode/constants';
const Detail = React.memo(({ repoID, path, currentMode, dirent, currentRepoInfo, repoTags, fileTags, onClose, onFileTagChanged }) => {
const isView = useMemo(() => currentMode === METADATA_MODE || path.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES), [currentMode, path]);
+ const isTag = useMemo(() => currentMode === TAGS_MODE || path.startsWith('/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES), [currentMode, path]);
useEffect(() => {
if (isView) return;
+ if (isTag) return;
// init context
const context = new MetadataContext();
@@ -24,9 +26,9 @@ const Detail = React.memo(({ repoID, path, currentMode, dirent, currentRepoInfo,
delete window['sfMetadataContext'];
}
};
- }, [repoID, currentRepoInfo, isView]);
+ }, [repoID, currentRepoInfo, isView, isTag]);
- if (currentMode === TAGS_MODE) return null;
+ if (isTag) return null;
if (isView) {
const viewId = path.split('/').pop();
diff --git a/frontend/src/components/dirent-list-view/dirent-list-view.js b/frontend/src/components/dirent-list-view/dirent-list-view.js
index 2e962b6039f..d7e4d426bf3 100644
--- a/frontend/src/components/dirent-list-view/dirent-list-view.js
+++ b/frontend/src/components/dirent-list-view/dirent-list-view.js
@@ -1,5 +1,6 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
+import classnames from 'classnames';
import { siteRoot, gettext, username, enableSeadoc, thumbnailSizeForOriginal, thumbnailDefaultSize, fileServerRoot } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import TextTranslation from '../../utils/text-translation';
@@ -20,6 +21,7 @@ import { EVENT_BUS_TYPE } from '../common/event-bus-type';
import EmptyTip from '../empty-tip';
import imageAPI from '../../utils/image-api';
import { seafileAPI } from '../../utils/seafile-api';
+import FixedWidthTable from '../common/fixed-width-table';
const propTypes = {
path: PropTypes.string.isRequired,
@@ -80,7 +82,6 @@ class DirentListView extends React.Component {
activeDirent: null,
isListDropTipShow: false,
isShowDirentsDraggablePreview: false,
- containerWidth: 0,
};
this.enteredCounter = 0; // Determine whether to enter the child element to avoid dragging bubbling bugs。
@@ -101,20 +102,12 @@ class DirentListView extends React.Component {
const { modify } = customPermission.permission;
this.canDrop = modify;
}
-
- this.containerRef = null;
}
componentDidMount() {
this.unsubscribeEvent = this.props.eventBus.subscribe(EVENT_BUS_TYPE.RESTORE_IMAGE, this.recalculateImageItems);
- this.resizeObserver = new ResizeObserver(this.handleResize);
- this.containerRef && this.resizeObserver.observe(this.containerRef);
}
- handleResize = () => {
- this.setState({ containerWidth: this.containerRef.offsetWidth - 32 });
- };
-
recalculateImageItems = () => {
if (!this.state.isImagePopupOpen) return;
let imageItems = this.props.direntList
@@ -129,7 +122,6 @@ class DirentListView extends React.Component {
componentWillUnmount() {
this.unsubscribeEvent();
- this.containerRef && this.resizeObserver.unobserve(this.containerRef);
}
freezeItem = () => {
@@ -687,18 +679,56 @@ class DirentListView extends React.Component {
});
};
- render() {
+ getHeaders = (isDesktop) => {
const { direntList, sortBy, sortOrder } = this.props;
- const { containerWidth } = this.state;
+ if (!isDesktop) {
+ return [
+ { isFixed: false, width: 0.12 },
+ { isFixed: false, width: 0.8 },
+ { isFixed: false, width: 0.08 },
+ ];
+ }
// sort
const sortByName = sortBy == 'name';
const sortByTime = sortBy == 'time';
const sortBySize = sortBy == 'size';
const sortIcon = sortOrder == 'asc' ?
:
;
+ return [
+ { isFixed: true, width: 31, className: 'pl10 pr-2', children: (
+
+ ) }, {
+ isFixed: true, width: 32, className: 'pl-2 pr-2', // star
+ }, {
+ isFixed: true, width: 40, className: 'pl-2 pr-2', // icon
+ }, {
+ isFixed: false, width: 0.5, children: (
{gettext('Name')} {sortByName && sortIcon}),
+ }, {
+ isFixed: false, width: 0.06, // tag
+ }, {
+ isFixed: false, width: 0.18, // operation
+ }, {
+ isFixed: false, width: 0.11, children: (
{gettext('Size')} {sortBySize && sortIcon})
+ }, {
+ isFixed: false, width: 0.15, children: (
{gettext('Last Update')} {sortByTime && sortIcon})
+ }
+ ];
+ };
+
+ render() {
+ const { direntList } = this.props;
const isDesktop = Utils.isDesktop();
const repoEncrypted = this.props.currentRepoInfo.encrypted;
+ const headers = this.getHeaders(isDesktop);
return (
this.containerRef = ref}
>
- {direntList.length > 0 &&
-
- }
+
+ )}
{direntList.length === 0 &&
}
diff --git a/frontend/src/pages/share-admin/folders.js b/frontend/src/pages/share-admin/folders.js
index e5be54b27db..a468f08687c 100644
--- a/frontend/src/pages/share-admin/folders.js
+++ b/frontend/src/pages/share-admin/folders.js
@@ -2,6 +2,7 @@ import React, { Fragment, Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
+import classnames from 'classnames';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import { gettext, siteRoot, isPro } from '../../utils/constants';
@@ -11,6 +12,7 @@ import toaster from '../../components/toast';
import SharePermissionEditor from '../../components/select-editor/share-permission-editor';
import SharedFolderInfo from '../../models/shared-folder-info';
import PermSelect from '../../components/dialog/perm-select';
+import FixedWidthTable from '../../components/common/fixed-width-table';
class Content extends Component {
@@ -26,51 +28,46 @@ class Content extends Component {
if (loading) {
return
;
- } else if (errorMsg) {
+ }
+ if (errorMsg) {
return
{errorMsg}
;
- } else {
- const emptyTip = (
+ }
+ if (!items.length) {
+ return (
);
+ }
- // sort
- const sortByName = sortBy == 'name';
- const sortIcon = sortOrder == 'asc' ?
:
;
-
- const isDesktop = Utils.isDesktop();
- const table = (
-
-
- {isDesktop ? (
-
- {/* icon*/} |
- {gettext('Name')} {sortByName && sortIcon} |
- {gettext('Share To')} |
- {gettext('Permission')} |
- |
-
- ) : (
-
- |
- |
- |
-
- )}
-
-
- {items.map((item, index) => {
- return ( );
- })}
-
-
- );
+ // sort
+ const sortByName = sortBy == 'name';
+ const sortIcon = sortOrder == 'asc' ?
:
;
- return items.length ? table : emptyTip;
- }
+ const isDesktop = Utils.isDesktop();
+
+ return (
+
{gettext('Name')} {sortByName && sortIcon}) },
+ { isFixed: false, width: 0.3, children: gettext('Share To') },
+ { isFixed: false, width: 0.25, children: gettext('Permission') },
+ { isFixed: false, width: 0.1 },
+ ] : [
+ { isFixed: false, width: 0.12 },
+ { isFixed: false, width: 0.8 },
+ { isFixed: false, width: 0.08 },
+ ]}
+ >
+ {items.map((item, index) => {
+ return ( );
+ })}
+
+ );
}
}
@@ -214,7 +211,7 @@ class Item extends Component {
if (this.props.isDesktop) {
return (
- ![{iconTitle} {iconTitle}]({iconUrl}) |
+ ![{iconTitle} {iconTitle}]({iconUrl}) |
{item.folder_name} |
{item.share_type == 'personal' ?
diff --git a/frontend/src/pages/share-admin/libraries.js b/frontend/src/pages/share-admin/libraries.js
index c1ef78e7e6a..5fd531d40bc 100644
--- a/frontend/src/pages/share-admin/libraries.js
+++ b/frontend/src/pages/share-admin/libraries.js
@@ -2,6 +2,7 @@ import React, { Fragment, Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
+import classnames from 'classnames';
import { seafileAPI } from '../../utils/seafile-api';
import { gettext, siteRoot, isPro } from '../../utils/constants';
import { Utils } from '../../utils/utils';
@@ -10,6 +11,7 @@ import EmptyTip from '../../components/empty-tip';
import SharePermissionEditor from '../../components/select-editor/share-permission-editor';
import SharedRepoInfo from '../../models/shared-repo-info';
import PermSelect from '../../components/dialog/perm-select';
+import FixedWidthTable from '../../components/common/fixed-width-table';
class Content extends Component {
@@ -25,55 +27,46 @@ class Content extends Component {
if (loading) {
return ;
- } else if (errorMsg) {
+ }
+ if (errorMsg) {
return {errorMsg} ;
- } else {
- const emptyTip = (
+ }
+
+ if (!items.length) {
+ return (
);
+ }
- // sort
- const sortByName = sortBy == 'name';
- const sortIcon = sortOrder == 'asc' ? : ;
-
- const isDesktop = Utils.isDesktop();
- const table = (
-
-
- {isDesktop ? (
-
- {/* icon*/} |
- {gettext('Name')} {sortByName && sortIcon} |
- {gettext('Share To')} |
- {gettext('Permission')} |
- |
-
- ) : (
-
- |
- |
- |
-
- )}
-
-
- {items.map((item, index) => {
- return ( );
- })}
-
-
- );
+ // sort
+ const sortByName = sortBy == 'name';
+ const sortIcon = sortOrder == 'asc' ? : ;
- return items.length ? table : emptyTip;
- }
+ const isDesktop = Utils.isDesktop();
+ return (
+ {gettext('Name')} {sortByName && sortIcon}) },
+ { isFixed: false, width: 0.3, children: gettext('Share To') },
+ { isFixed: false, width: 0.25, children: gettext('Permission') },
+ { isFixed: false, width: 0.1 },
+ ] : [
+ { isFixed: false, width: 0.12 },
+ { isFixed: false, width: 0.8 },
+ { isFixed: false, width: 0.08 },
+ ]}
+ >
+ {items.map((item, index) => {
+ return ( );
+ })}
+
+ );
}
}
@@ -228,7 +221,7 @@ class Item extends Component {
if (this.props.isDesktop) {
return (
|
- ![{iconTitle} {iconTitle}]({iconUrl}) |
+ ![{iconTitle} {iconTitle}]({iconUrl}) |
{item.repo_name} |
{item.share_type == 'personal' ? {shareTo} : shareTo}
diff --git a/frontend/src/pages/share-admin/share-links.js b/frontend/src/pages/share-admin/share-links.js
index f359ad1fa9a..4b7289c34b4 100644
--- a/frontend/src/pages/share-admin/share-links.js
+++ b/frontend/src/pages/share-admin/share-links.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import dayjs from 'dayjs';
import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
+import classnames from 'classnames';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import { isPro, gettext, siteRoot, canGenerateUploadLink } from '../../utils/constants';
@@ -16,6 +17,7 @@ import SortOptionsDialog from '../../components/dialog/sort-options';
import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
import Selector from '../../components/single-selector';
import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar';
+import FixedWidthTable from '../../components/common/fixed-width-table';
const contentPropTypes = {
loading: PropTypes.bool.isRequired,
@@ -60,69 +62,60 @@ class Content extends Component {
if (loading) {
return ;
- } else if (errorMsg) {
+ }
+ if (errorMsg) {
return {errorMsg} ;
- } else {
- const emptyTip = (
+ }
+ if (!items.length) {
+ return (
-
+ />
);
+ }
- // sort
- const sortByName = sortBy == 'name';
- const sortByTime = sortBy == 'time';
- const sortIcon = sortOrder == 'asc' ? : ;
-
- const isDesktop = Utils.isDesktop();
- // only for some columns
- const columnWidths = isPro ? ['14%', '7%', '14%'] : ['21%', '14%', '20%'];
- const table = (
-
- );
+ // sort
+ const sortByName = sortBy == 'name';
+ const sortByTime = sortBy == 'time';
+ const sortIcon = sortOrder == 'asc' ? : ;
- return items.length ? (
- <>
- {table}
- {this.props.isLoadingMore && }
- >
- ) : emptyTip;
- }
+ const isDesktop = Utils.isDesktop();
+ // only for some columns
+ const columnWidths = isPro ? [0.14, 0.07, 0.14] : [0.21, 0.14, 0.2];
+
+ return (
+ <>
+ {gettext('Name')} {sortByName && sortIcon}) },
+ { isFixed: false, width: columnWidths[0], children: gettext('Library') },
+ isPro ? { isFixed: false, width: 0.2, children: gettext('Permission') } : null,
+ { isFixed: false, width: columnWidths[1], children: gettext('Visits') },
+ { isFixed: false, width: columnWidths[2], children: ({gettext('Expiration')} {sortByTime && sortIcon}) },
+ { isFixed: false, width: 0.1 }, // operations
+ ].filter(i => i) : [
+ { isFixed: false, width: 0.12 },
+ { isFixed: false, width: 0.8 },
+ { isFixed: false, width: 0.08 },
+ ]}
+ >
+ {items.map((item, index) => {
+ return ( );
+ })}
+
+ {this.props.isLoadingMore && }
+ >
+ );
}
}
@@ -277,7 +270,7 @@ class Item extends Component {
onMouseLeave={this.handleMouseLeave}
onFocus={this.handleMouseEnter}
>
- | ![]({iconUrl}) |
+ ![]({iconUrl}) |
{item.is_dir ?
{item.obj_name} :
diff --git a/frontend/src/pages/share-admin/upload-links.js b/frontend/src/pages/share-admin/upload-links.js
index 0f9b6105843..3e6adba32a6 100644
--- a/frontend/src/pages/share-admin/upload-links.js
+++ b/frontend/src/pages/share-admin/upload-links.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import dayjs from 'dayjs';
import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
+import classnames from 'classnames';
import { gettext, siteRoot, canGenerateShareLink } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
@@ -13,6 +14,7 @@ import UploadLink from '../../models/upload-link';
import ShareAdminLink from '../../components/dialog/share-admin-link';
import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar';
+import FixedWidthTable from '../../components/common/fixed-width-table';
const contentPropTypes = {
loading: PropTypes.bool.isRequired,
@@ -32,45 +34,37 @@ class Content extends Component {
if (errorMsg) {
return {errorMsg} ;
}
-
- const emptyTip = (
-
-
- );
+ if (!items.length) {
+ return (
+
+ );
+ }
const isDesktop = Utils.isDesktop();
- const table = (
-
-
- {isDesktop ? (
-
- {/* icon*/} |
- {gettext('Name')} |
- {gettext('Library')} |
- {gettext('Visits')} |
- {gettext('Expiration')} |
- {/* Operations*/} |
-
- ) : (
-
- |
- |
- |
-
- )}
-
-
- {items.map((item, index) => {
- return (- );
- })}
-
-
+ return (
+
+ {items.map((item, index) => {
+ return (- );
+ })}
+
);
-
- return items.length ? table : emptyTip;
}
}
@@ -145,7 +139,7 @@ class Item extends Component {
{this.props.isDesktop ?
- ![]({iconUrl}) |
+ ![]({iconUrl}) |
{item.obj_name}
{item.obj_id === '' ? {gettext('(deleted)')} : null}
diff --git a/frontend/src/tag/views/tag-files/index.js b/frontend/src/tag/views/tag-files/index.js
index eacf2c2407d..47366853555 100644
--- a/frontend/src/tag/views/tag-files/index.js
+++ b/frontend/src/tag/views/tag-files/index.js
@@ -1,10 +1,11 @@
-import React, { useCallback, useState, useRef, useEffect } from 'react';
+import React, { useCallback, useState, useRef } from 'react';
import { useTagView, useTags } from '../../hooks';
import { gettext } from '../../../utils/constants';
import TagFile from './tag-file';
import { getRecordIdFromRecord } from '../../../metadata/utils/cell';
import EmptyTip from '../../../components/empty-tip';
import ImagePreviewer from '../../../metadata/components/cell-formatter/image-previewer';
+import FixedWidthTable from '../../../components/common/fixed-width-table';
import './index.css';
@@ -13,10 +14,8 @@ const TagFiles = () => {
const { tagsData } = useTags();
const [selectedFiles, setSelectedFiles] = useState(null);
const [isImagePreviewerVisible, setImagePreviewerVisible] = useState(false);
- const [containerWidth, setContainerWidth] = useState(0);
const currentImageRef = useRef(null);
- const containerRef = useRef(null);
const onMouseDown = useCallback((event) => {
if (event.button === 2) {
@@ -66,68 +65,77 @@ const TagFiles = () => {
setImagePreviewerVisible(false);
}, []);
- useEffect(() => {
- const container = containerRef.current;
- const handleResize = () => {
- if (!container) return;
- // 32: container padding left + container padding right
- setContainerWidth(container.offsetWidth - 32);
- };
- const resizeObserver = new ResizeObserver(handleResize);
- container && resizeObserver.observe(container);
-
- return () => {
- container && resizeObserver.unobserve(container);
- };
- }, []);
-
if (tagFiles.rows.length === 0) {
return ();
}
const isSelectedAll = selectedFiles && selectedFiles.length === tagFiles.rows.length;
+ const headers = [
+ {
+ isFixed: true,
+ width: 31,
+ className: 'pl10 pr-2',
+ children: (
+
+ )
+ }, {
+ isFixed: true,
+ width: 41,
+ className: 'pl-2 pr-2',
+ }, {
+ isFixed: false,
+ width: 0.5,
+ children: ({gettext('Name')}),
+ }, {
+ isFixed: false,
+ width: 0.06,
+ }, {
+ isFixed: false,
+ width: 0.18,
+ }, {
+ isFixed: false,
+ width: 0.11,
+ children: ({gettext('Size')}),
+ }, {
+ isFixed: false,
+ width: 0.15,
+ children: ({gettext('Last Update')}),
+ }
+ ];
return (
<>
-
-
+
+
+ {tagFiles.rows.map(file => {
+ const fileId = getRecordIdFromRecord(file);
+ return (
+ );
+ })}
+
{isImagePreviewerVisible && (
| |