diff --git a/frontend/src/assets/icons/sort-ascending.svg b/frontend/src/assets/icons/sort-ascending.svg
new file mode 100644
index 0000000000..b8721f7eb7
--- /dev/null
+++ b/frontend/src/assets/icons/sort-ascending.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/assets/icons/sort-descending.svg b/frontend/src/assets/icons/sort-descending.svg
new file mode 100644
index 0000000000..0564784841
--- /dev/null
+++ b/frontend/src/assets/icons/sort-descending.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/metadata/metadata-view/_basic/constants/sort.js b/frontend/src/metadata/metadata-view/_basic/constants/sort.js
index 998309d5ed..6451b49b21 100644
--- a/frontend/src/metadata/metadata-view/_basic/constants/sort.js
+++ b/frontend/src/metadata/metadata-view/_basic/constants/sort.js
@@ -19,6 +19,13 @@ const SORT_COLUMN_OPTIONS = [
CellType.RATE,
];
+const SHOW_DISABLED_SORT_COLUMNS = [
+ CellType.LONG_TEXT,
+ CellType.GEOLOCATION,
+ CellType.CREATOR,
+ CellType.LAST_MODIFIER,
+];
+
const GALLERY_SORT_COLUMN_OPTIONS = [
CellType.CTIME,
CellType.MTIME,
@@ -38,6 +45,7 @@ const NUMBER_SORTER_COLUMN_TYPES = [CellType.NUMBER, CellType.RATE];
export {
SORT_TYPE,
SORT_COLUMN_OPTIONS,
+ SHOW_DISABLED_SORT_COLUMNS,
GALLERY_SORT_COLUMN_OPTIONS,
GALLERY_FIRST_SORT_COLUMN_OPTIONS,
TEXT_SORTER_COLUMN_TYPES,
diff --git a/frontend/src/metadata/metadata-view/components/cell-editor/rate-editor/rate-item.js b/frontend/src/metadata/metadata-view/components/cell-editor/rate-editor/rate-item.js
index e4007feb58..a49b2bd8d0 100644
--- a/frontend/src/metadata/metadata-view/components/cell-editor/rate-editor/rate-item.js
+++ b/frontend/src/metadata/metadata-view/components/cell-editor/rate-editor/rate-item.js
@@ -53,7 +53,7 @@ const RateItem = ({
{enterIndex !== -1 && (
-
+
{enterIndex}
)}
diff --git a/frontend/src/metadata/metadata-view/components/data-process-setter/sort-setter.js b/frontend/src/metadata/metadata-view/components/data-process-setter/sort-setter.js
index 647b3d8515..1fae22e0c7 100644
--- a/frontend/src/metadata/metadata-view/components/data-process-setter/sort-setter.js
+++ b/frontend/src/metadata/metadata-view/components/data-process-setter/sort-setter.js
@@ -1,10 +1,11 @@
-import React, { useCallback, useState, useMemo } from 'react';
+import React, { useCallback, useState, useMemo, useEffect } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { IconBtn } from '@seafile/sf-metadata-ui-component';
import { getValidSorts, CommonlyUsedHotkey } from '../../_basic';
import { gettext } from '../../utils';
import { SortPopover } from '../popover';
+import { EVENT_BUS_TYPE } from '../../constants';
const SortSetter = ({ target, type, sorts: propsSorts, readOnly, columns, isNeedSubmit, wrapperClass, modifySorts }) => {
const [isShowSetter, setShowSetter] = useState(false);
@@ -20,6 +21,19 @@ const SortSetter = ({ target, type, sorts: propsSorts, readOnly, columns, isNeed
return isNeedSubmit ? gettext('Preset sort') : gettext('Sort');
}, [isNeedSubmit, sorts]);
+ const displaySetter = useCallback(() => {
+ setShowSetter(true);
+ }, []);
+
+ useEffect(() => {
+ const eventBus = window.sfMetadataContext.eventBus;
+ const unsubscribeDisplaySorts = eventBus.subscribe(EVENT_BUS_TYPE.DISPLAY_SORTS, displaySetter);
+ return () => {
+ unsubscribeDisplaySorts();
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
const onSetterToggle = useCallback(() => {
setShowSetter(!isShowSetter);
}, [isShowSetter]);
diff --git a/frontend/src/metadata/metadata-view/components/popover/filter-popover/advanced-filters/filter-item/index.js b/frontend/src/metadata/metadata-view/components/popover/filter-popover/advanced-filters/filter-item/index.js
index fb1f1bb0eb..32366c5ffb 100644
--- a/frontend/src/metadata/metadata-view/components/popover/filter-popover/advanced-filters/filter-item/index.js
+++ b/frontend/src/metadata/metadata-view/components/popover/filter-popover/advanced-filters/filter-item/index.js
@@ -534,6 +534,7 @@ class FilterItem extends React.Component {
target={this.invalidFilterTip}
placement='bottom'
fade={false}
+ className="sf-metadata-tooltip"
>
{gettext('Invalid filter')}
@@ -608,7 +609,7 @@ class FilterItem extends React.Component {
{/* {showToolTip && (
-
+
{gettext('If there are multiple items in the cell, a random one will be chosen and be compared with the filter value.')}
diff --git a/frontend/src/metadata/metadata-view/components/popover/options-popover/options-footer.js b/frontend/src/metadata/metadata-view/components/popover/options-popover/options-footer.js
index 1f57334978..b4fe2e8ce4 100644
--- a/frontend/src/metadata/metadata-view/components/popover/options-popover/options-footer.js
+++ b/frontend/src/metadata/metadata-view/components/popover/options-popover/options-footer.js
@@ -84,7 +84,7 @@ export default class OptionFooter extends React.Component {
return (
-
+
{gettext('Use the import/export function to transfer options quickly. (The export is in JSON format.) By pasting cells, copied from a text column, an Excel or a TXT file, you can also add options quickly.')}
{
fade={false}
delay={{ show: 0, hide: 0 }}
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
+ className="sf-metadata-tooltip"
>
{isDir ? gettext('Open folder') : gettext('Open file')}
diff --git a/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/dropdown-menu/dropdown-item.js b/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/dropdown-menu/dropdown-item.js
index 4ce2033f0d..8a93ff0553 100644
--- a/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/dropdown-menu/dropdown-item.js
+++ b/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/dropdown-menu/dropdown-item.js
@@ -41,7 +41,7 @@ const ColumnDropdownItem = ({ disabled, iconName, target, title, tip, className,
{title}
{isShowToolTip && (
-
+
{tip}
)}
diff --git a/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/dropdown-menu/index.js b/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/dropdown-menu/index.js
index 8c1d81058c..e6ed75d98f 100644
--- a/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/dropdown-menu/index.js
+++ b/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/dropdown-menu/index.js
@@ -5,12 +5,15 @@ import classnames from 'classnames';
import { ModalPortal, Icon } from '@seafile/sf-metadata-ui-component';
import { isMobile, gettext } from '../../../../../../../../utils';
import DropdownItem from './dropdown-item';
-import { CellType, DEFAULT_DATE_FORMAT, getDateDisplayString } from '../../../../../../../../_basic';
+import { CellType, DEFAULT_DATE_FORMAT, SORT_COLUMN_OPTIONS, SHOW_DISABLED_SORT_COLUMNS, SORT_TYPE,
+ getDateDisplayString
+} from '../../../../../../../../_basic';
import { RenamePopover, OptionsPopover } from '../../../../../../../popover';
+import { EVENT_BUS_TYPE } from '../../../../../../../../constants';
import './index.css';
-const HeaderDropdownMenu = ({ column, renameColumn, modifyColumnData, deleteColumn }) => {
+const HeaderDropdownMenu = ({ column, view, renameColumn, modifyColumnData, deleteColumn }) => {
const menuRef = createRef();
const dropdownDomRef = createRef();
const [isMenuShow, setMenuShow] = useState(false);
@@ -153,11 +156,38 @@ const HeaderDropdownMenu = ({ column, renameColumn, modifyColumnData, deleteColu
);
}, [today, column, isMenuShow, isSubMenuShow, onChangeDateFormat, openSubMenu]);
+ const modifySort = useCallback((type, event) => {
+ const canModifyView = window.sfMetadataContext.canModifyView();
+ if (!canModifyView) {
+ event.stopPropagation();
+ return;
+ }
+ const sorts = view.sorts.slice(0);
+ const { key } = column;
+ const sortIndex = sorts.findIndex(sort => sort.column_key === key);
+ const sort = sorts[sortIndex];
+ const newSort = { column_key: column.key, sort_type: type };
+ const eventBus = window.sfMetadataContext.eventBus;
+ if (!sort) {
+ sorts.push(newSort);
+ eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_SORTS, sorts, true);
+ return;
+ }
+ if (sort && sort.sort_type !== type) {
+ sorts.splice(sortIndex, 1, newSort);
+ eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_SORTS, sorts, true);
+ return;
+ }
+ eventBus.dispatch(EVENT_BUS_TYPE.DISPLAY_SORTS);
+ }, [view, column]);
+
const renderDropdownMenu = useCallback(() => {
const { type } = column;
const canModifyColumnData = window.sfMetadataContext.canModifyColumnData(column);
const canDeleteColumn = window.sfMetadataContext.canDeleteColumn(column);
const canRenameColumn = window.sfMetadataContext.canRenameColumn(column);
+ const canModifyView = window.sfMetadataContext.canModifyView();
+
return (
@@ -216,6 +246,28 @@ const HeaderDropdownMenu = ({ column, renameColumn, modifyColumnData, deleteColu
onChange={openRenamePopover}
onMouseEnter={hideSubMenu}
/>
+ {(SORT_COLUMN_OPTIONS.includes(column.type) || SHOW_DISABLED_SORT_COLUMNS.includes(column.type)) && (
+ <>
+
modifySort(SORT_TYPE.UP)}
+ onMouseEnter={hideSubMenu}
+ />
+ modifySort(SORT_TYPE.DOWN)}
+ onMouseEnter={hideSubMenu}
+ />
+ >
+ )}
);
- }, [column, openRenamePopover, hideSubMenu, renderDateFormat, openOptionPopover, menuRef, dropdownDomRef, onDelete]);
+ }, [column, openRenamePopover, hideSubMenu, renderDateFormat, openOptionPopover, menuRef, dropdownDomRef, modifySort, onDelete]);
return (
<>
diff --git a/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/index.js b/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/index.js
index ef9e4f300e..ddae5c9926 100644
--- a/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/index.js
+++ b/frontend/src/metadata/metadata-view/components/view/table/table-main/records/records-header/cell/index.js
@@ -89,6 +89,7 @@ const Cell = ({
isHideTriangle,
column,
style: propsStyle,
+ view,
renameColumn,
deleteColumn,
modifyColumnData,
@@ -99,8 +100,8 @@ const Cell = ({
const canEditColumnInfo = useMemo(() => {
if (isHideTriangle) return false;
- return window.sfMetadataContext.canModifyColumn(column);
- }, [isHideTriangle, column]);
+ return window.sfMetadataContext.canModify();
+ }, [isHideTriangle]);
const style = useMemo(() => {
const { left, width } = column;
@@ -159,7 +160,7 @@ const Cell = ({
-
+
{gettext(headerIconTooltip)}
@@ -169,6 +170,7 @@ const Cell = ({
{canEditColumnInfo && (
{
+ if (this.permission === 'r') return false;
+ return true;
+ };
+
canModifyRow = (row) => {
if (this.permission === 'r') return false;
return true;
diff --git a/frontend/src/metadata/metadata-view/hooks/metadata.js b/frontend/src/metadata/metadata-view/hooks/metadata.js
index 8c50ed2a7c..2cb1de1701 100644
--- a/frontend/src/metadata/metadata-view/hooks/metadata.js
+++ b/frontend/src/metadata/metadata-view/hooks/metadata.js
@@ -49,8 +49,8 @@ export const MetadataProvider = ({
window.sfMetadataStore.modifyFilters(filterConjunction, filters, basicFilters);
}, []);
- const modifySorts = useCallback((sorts) => {
- window.sfMetadataStore.modifySorts(sorts);
+ const modifySorts = useCallback((sorts, displaySorts = false) => {
+ window.sfMetadataStore.modifySorts(sorts, displaySorts);
}, []);
const modifyGroupbys = useCallback((groupbys) => {
diff --git a/frontend/src/metadata/metadata-view/store/index.js b/frontend/src/metadata/metadata-view/store/index.js
index ce207ef4fe..48cf1c9445 100644
--- a/frontend/src/metadata/metadata-view/store/index.js
+++ b/frontend/src/metadata/metadata-view/store/index.js
@@ -360,7 +360,7 @@ class Store {
this.applyOperation(operation);
}
- modifySorts(sorts) {
+ modifySorts(sorts, displaySorts = false) {
const type = OPERATION_TYPE.MODIFY_SORTS;
const operation = this.createOperation({
type,
@@ -369,6 +369,7 @@ class Store {
view_id: this.viewId,
success_callback: () => {
this.context.eventBus.dispatch(EVENT_BUS_TYPE.RELOAD_DATA);
+ displaySorts && this.context.eventBus.dispatch(EVENT_BUS_TYPE.DISPLAY_SORTS);
}
});
this.applyOperation(operation);