diff --git a/frontend/src/metadata/metadata-view/components/data-process-setter/hide-column-setter.jsx b/frontend/src/metadata/metadata-view/components/data-process-setter/hide-column-setter.jsx
index a07aa9fb85..1528568167 100644
--- a/frontend/src/metadata/metadata-view/components/data-process-setter/hide-column-setter.jsx
+++ b/frontend/src/metadata/metadata-view/components/data-process-setter/hide-column-setter.jsx
@@ -6,7 +6,7 @@ import { CommonlyUsedHotkey } from '../../_basic';
import { gettext } from '../../utils';
import { HideColumnPopover } from '../popover';
-const HideColumnSetter = ({ readOnly, columns, wrapperClass, target, hiddenColumns, modifyHiddenColumns }) => {
+const HideColumnSetter = ({ readOnly, columns, wrapperClass, target, hiddenColumns, modifyHiddenColumns, modifyColumnOrder }) => {
const [isShowSetter, setShowSetter] = useState(false);
const validHiddenColumns = useMemo(() => {
@@ -57,6 +57,7 @@ const HideColumnSetter = ({ readOnly, columns, wrapperClass, target, hiddenColum
columns={columns}
hidePopover={onSetterToggle}
onChange={onChange}
+ modifyColumnOrder={modifyColumnOrder}
/>
)}
>
@@ -70,6 +71,7 @@ HideColumnSetter.propTypes = {
hiddenColumns: PropTypes.array,
columns: PropTypes.array,
modifyHiddenColumns: PropTypes.func,
+ modifyColumnOrder: PropTypes.func,
};
export default HideColumnSetter;
diff --git a/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/hidden-columns/hide-column.js b/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/hidden-columns/hide-column.js
index b3d0d56b06..60a70bdcb6 100644
--- a/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/hidden-columns/hide-column.js
+++ b/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/hidden-columns/hide-column.js
@@ -1,10 +1,66 @@
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
+import { DragSource, DropTarget } from 'react-dnd';
import { Icon, Switch } from '@seafile/sf-metadata-ui-component';
import { COLUMNS_ICON_CONFIG } from '../../../../_basic';
import classNames from 'classnames';
-const HideColumnItem = ({ readOnly, column, isHidden, onChange }) => {
+const dragSource = {
+ beginDrag: props => {
+ return { key: props.column.key, column: props.column };
+ },
+ endDrag(props, monitor) {
+ const source = monitor.getItem();
+ const didDrop = monitor.didDrop();
+ let target = {};
+ if (!didDrop) {
+ return { source, target };
+ }
+ },
+ isDragging(props) {
+ const { columnIndex, currentIndex } = props;
+ return currentIndex > columnIndex;
+ }
+};
+const dropTarget = {
+ drop(props, monitor) {
+ const source = monitor.getItem();
+ const { column: targetColumn } = props;
+ if (targetColumn.key !== source.key && source.column.frozen === targetColumn.frozen) {
+ const target = { key: targetColumn.key };
+ props.onMove(source.key, target.key);
+ }
+ }
+};
+
+const dragCollect = (connect, monitor) => ({
+ connectDragSource: connect.dragSource(),
+ connectDragPreview: connect.dragPreview(),
+ isDragging: monitor.isDragging(),
+});
+
+const dropCollect = (connect, monitor) => ({
+ connectDropTarget: connect.dropTarget(),
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ dragged: monitor.getItem(),
+});
+
+const HideColumnItem = ({
+ isOver,
+ isDragging,
+ canDrop,
+ connectDragSource,
+ connectDragPreview,
+ connectDropTarget,
+ readOnly,
+ column,
+ columnIndex,
+ isHidden,
+ onChange,
+ onMouseEnter,
+ onMouseLeave,
+}) => {
const update = useCallback(() => {
if (readOnly) return;
@@ -12,28 +68,58 @@ const HideColumnItem = ({ readOnly, column, isHidden, onChange }) => {
}, [readOnly, column, onChange]);
return (
-
{isEmpty &&
{gettext('No properties available to be hidden')}
}
- {!isEmpty && columns.map((column) => {
+ {!isEmpty && columns.map((column, columnIndex) => {
return (
);
})}
@@ -33,6 +51,11 @@ HiddenColumns.propTypes = {
hiddenColumns: PropTypes.array,
columns: PropTypes.array,
onChange: PropTypes.func,
+ modifyColumnOrder: PropTypes.func,
};
-export default HiddenColumns;
+const DndHiddenColumns = DropTarget('sfMetadataHiddenColumns', {}, connect => ({
+ connectDropTarget: connect.dropTarget()
+}))(HiddenColumns);
+
+export default html5DragDropContext(DndHiddenColumns);
diff --git a/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/index.css b/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/index.css
index 98b9e18783..00438adf5a 100644
--- a/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/index.css
+++ b/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/index.css
@@ -64,7 +64,11 @@
cursor: pointer;
}
-.sf-metadata-hide-columns-popover .hide-column-item .hide-column-item-switch,
+.sf-metadata-hide-columns-popover .hide-column-item .hide-column-item-switch {
+ flex: 1;
+ overflow: hidden;
+}
+
.sf-metadata-hide-columns-popover .hide-column-item .hide-column-item-switch .custom-switch {
width: 100%;
}
@@ -79,7 +83,7 @@
.sf-metadata-hide-columns-popover .hide-column-item .custom-switch .custom-switch-description {
margin-left: 0;
padding-right: 5px;
- width: 212px;
+ flex: 1;
color: #212529;
transition: .3s color;
}
@@ -127,3 +131,34 @@
color: #212529;
cursor: pointer;
}
+
+.sf-metadata-hide-columns-popover .hide-column-item .drag-hide-column-handle {
+ width: 14px;
+ margin-right: 10px;
+}
+
+.sf-metadata-hide-columns-popover .hide-column-item .drag-hide-column-handle .sf-metadata-icon {
+ margin-right: 0px;
+}
+
+.sf-metadata-hide-columns-popover .hide-columns-list .hide-column-item.disabled .drag-hide-column-handle {
+ display: none;
+}
+
+.sf-metadata-hide-columns-popover .hide-column-item.hide-column-can-drop::after,
+.sf-metadata-hide-columns-popover .hide-column-item.hide-column-can-drop-top::before {
+ content: '';
+ height: 1px;
+ width: 100%;
+ position: absolute;
+ left: 0;
+ background: #666;
+}
+
+.sf-metadata-hide-columns-popover .hide-column-item.hide-column-can-drop::after {
+ bottom: 0;
+}
+
+.sf-metadata-hide-columns-popover .hide-column-item.hide-column-can-drop-top::before {
+ top: 0;
+}
diff --git a/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/index.js b/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/index.js
index 4f76f22db5..c4768f7fd1 100644
--- a/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/index.js
+++ b/frontend/src/metadata/metadata-view/components/popover/hidden-column-popover/index.js
@@ -9,7 +9,7 @@ import { getEventClassName, gettext } from '../../../utils';
import './index.css';
-const HideColumnPopover = ({ hidePopover, onChange, readOnly, target, placement, columns, hiddenColumns: oldHiddenColumns }) => {
+const HideColumnPopover = ({ hidePopover, onChange, readOnly, target, placement, columns, hiddenColumns: oldHiddenColumns, modifyColumnOrder }) => {
const [searchValue, setSearchValue] = useState('');
const [hiddenColumns, setHiddenColumns] = useState(oldHiddenColumns);
const displayColumns = useMemo(() => {
@@ -104,7 +104,7 @@ const HideColumnPopover = ({ hidePopover, onChange, readOnly, target, placement,
-
+
{!readOnly && !searchValue && (
{gettext('Hide all')}
@@ -125,6 +125,7 @@ HideColumnPopover.propTypes = {
columns: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
hidePopover: PropTypes.func.isRequired,
+ modifyColumnOrder: PropTypes.func,
};
export default HideColumnPopover;
diff --git a/frontend/src/metadata/metadata-view/components/table/container.js b/frontend/src/metadata/metadata-view/components/table/container.js
index 865e891209..38f60d278d 100644
--- a/frontend/src/metadata/metadata-view/components/table/container.js
+++ b/frontend/src/metadata/metadata-view/components/table/container.js
@@ -132,6 +132,10 @@ const Container = () => {
store.modifyColumnWidth(columnKey, newWidth);
}, [store]);
+ const modifyColumnOrder = useCallback((sourceColumnKey, targetColumnKey) => {
+ store.modifyColumnOrder(sourceColumnKey, targetColumnKey);
+ }, [store]);
+
const recordGetterById = useCallback((recordId) => {
return metadata.id_row_map[recordId];
}, [metadata]);
@@ -159,16 +163,19 @@ const Container = () => {
useEffect(() => {
document.addEventListener('keydown', onKeyDown);
- const unsubscribeModifyFilters = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_FILTERS, modifyFilters);
- const unsubscribeModifySorts = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_SORTS, modifySorts);
- const unsubscribeModifyGroupbys = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_GROUPBYS, modifyGroupbys);
- const unsubscribeModifyHiddenColumns = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_HIDDEN_COLUMNS, modifyHiddenColumns);
+ const eventBus = window.sfMetadataContext.eventBus;
+ const unsubscribeModifyFilters = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_FILTERS, modifyFilters);
+ const unsubscribeModifySorts = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_SORTS, modifySorts);
+ const unsubscribeModifyGroupbys = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_GROUPBYS, modifyGroupbys);
+ const unsubscribeModifyHiddenColumns = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_HIDDEN_COLUMNS, modifyHiddenColumns);
+ const unsubscribeModifyColumnOrder = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_COLUMN_ORDER, modifyColumnOrder);
return () => {
document.removeEventListener('keydown', onKeyDown);
unsubscribeModifyFilters();
unsubscribeModifySorts();
unsubscribeModifyGroupbys();
unsubscribeModifyHiddenColumns();
+ unsubscribeModifyColumnOrder();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -197,6 +204,7 @@ const Container = () => {
deleteColumn={deleteColumn}
modifyColumnData={modifyColumnData}
modifyColumnWidth={modifyColumnWidth}
+ modifyColumnOrder={modifyColumnOrder}
/>
)}
{metadata.view.type === 'image' && (
)}
diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/index.js b/frontend/src/metadata/metadata-view/components/table/table-main/records/index.js
index 1c0aa2c674..e05f81add3 100644
--- a/frontend/src/metadata/metadata-view/components/table/table-main/records/index.js
+++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/index.js
@@ -655,7 +655,7 @@ class Records extends Component {
render() {
const { recordIds, recordsCount, table, isGroupView, groupOffsetLeft, renameColumn, modifyColumnData,
- deleteColumn } = this.props;
+ deleteColumn, modifyColumnOrder } = this.props;
const { recordMetrics, columnMetrics, selectedRange, colOverScanStartIdx, colOverScanEndIdx } = this.state;
const { columns, totalWidth, lastFrozenColumnKey } = columnMetrics;
const containerWidth = totalWidth + SEQUENCE_COLUMN_WIDTH + CANVAS_RIGHT_INTERVAL + groupOffsetLeft;
@@ -689,6 +689,7 @@ class Records extends Component {
renameColumn={renameColumn}
deleteColumn={deleteColumn}
modifyColumnData={modifyColumnData}
+ modifyColumnOrder={modifyColumnOrder}
/>
{this.renderRecordsBody({ containerWidth })}
@@ -746,6 +747,7 @@ Records.propTypes = {
deleteColumn: PropTypes.func,
modifyColumnData: PropTypes.func,
modifyColumnWidth: PropTypes.func,
+ modifyColumnOrder: PropTypes.func,
getCopiedRecordsAndColumnsFromRange: PropTypes.func,
};
diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/cell/index.css b/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/cell/index.css
index 724ab9ab32..dc09218d00 100644
--- a/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/cell/index.css
+++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/cell/index.css
@@ -7,3 +7,11 @@
font-size: 12px;
transform: scale(.8);
}
+
+.sf-metadata-record-header-cell .rdg-can-drop > .sf-metadata-result-table-cell.column {
+ cursor: move;
+}
+
+.sf-metadata-record-header-cell .rdg-dropping > .sf-metadata-result-table-cell.column {
+ background: #ececec;
+}
diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/cell/index.js b/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/cell/index.js
index 190c2bccb4..699042e03a 100644
--- a/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/cell/index.js
+++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/cell/index.js
@@ -2,8 +2,9 @@ import React, { useRef, useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { UncontrolledTooltip } from 'reactstrap';
+import { DragSource, DropTarget } from 'react-dnd';
import { Icon } from '@seafile/sf-metadata-ui-component';
-import { COLUMNS_ICON_CONFIG, COLUMNS_ICON_NAME } from '../../../../../../_basic';
+import { COLUMNS_ICON_CONFIG, COLUMNS_ICON_NAME, PRIVATE_COLUMN_KEY } from '../../../../../../_basic';
import ResizeColumnHandle from './resize-column-handle';
import { EVENT_BUS_TYPE } from '../../../../../../constants';
import DropdownMenu from './dropdown-menu';
@@ -11,7 +12,73 @@ import { gettext } from '../../../../../../utils';
import './index.css';
+
+const dragSource = {
+ beginDrag: props => {
+ return { key: props.column.key, column: props.column };
+ },
+ endDrag(props, monitor) {
+ const source = monitor.getItem();
+ const didDrop = monitor.didDrop();
+ let target = {};
+ if (!didDrop) {
+ return { source, target };
+ }
+ },
+ isDragging(props) {
+ const { column, dragged } = props;
+ const { key } = dragged;
+ return key === column.key;
+ }
+};
+
+const dropTarget = {
+ hover(props, monitor, component) {
+ // This is fired very often and lets you perform side effects.
+ if (!window.sfMetadataBody) return;
+ const defaultColumnWidth = 200;
+ const offsetX = monitor.getClientOffset().x;
+ const width = document.querySelector('.sf-metadata-wrapper')?.clientWidth;
+ const left = window.innerWidth - width;
+ if (offsetX > width - defaultColumnWidth) {
+ window.sfMetadataBody.scrollToRight();
+ } else if (offsetX < props.frozenColumnsWidth + defaultColumnWidth + left) {
+ window.sfMetadataBody.scrollToLeft();
+ } else {
+ window.sfMetadataBody.clearHorizontalScroll();
+ }
+ },
+ drop(props, monitor) {
+ const source = monitor.getItem();
+ const { column: targetColumn } = props;
+ if (targetColumn.key !== source.key && source.column.frozen === targetColumn.frozen) {
+ let target = { key: targetColumn.key };
+ props.onMove(source, target);
+ window.sfMetadataBody.clearHorizontalScroll();
+ }
+ }
+};
+
+const dragCollect = (connect, monitor) => ({
+ connectDragSource: connect.dragSource(),
+ connectDragPreview: connect.dragPreview(),
+ isDragging: monitor.isDragging(),
+});
+
+const dropCollect = (connect, monitor) => ({
+ connectDropTarget: connect.dropTarget(),
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ dragged: monitor.getItem(),
+});
+
const Cell = ({
+ isOver,
+ isDragging,
+ canDrop,
+ connectDragSource,
+ connectDragPreview,
+ connectDropTarget,
frozen,
groupOffsetLeft,
isLastFrozenCell,
@@ -68,41 +135,67 @@ const Cell = ({
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SELECT_COLUMN, column);
}, []);
+ const onContextMenu = useCallback((event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ }, []);
+
const { key, name, type } = column;
const headerIconTooltip = COLUMNS_ICON_NAME[type];
- return (
-
-
handleHeaderCellClick(column, frozen)}
- >
-
-
-
- {gettext(headerIconTooltip)}
-
-
- {name}
-
+ const canModifyColumnOrder = window.sfMetadataContext.canModifyColumnOrder();
+ const cell = (
+
);
+ if (!canModifyColumnOrder || column.key === PRIVATE_COLUMN_KEY.FILE_NAME) {
+ return (
+
+ {cell}
+
+ );
+ }
+
+ return (
+
+ {connectDropTarget(
+ connectDragPreview(
+ connectDragSource(
+
+ {cell}
+
+ )
+ )
+ )}
+
+ );
};
Cell.defaultProps = {
@@ -123,4 +216,6 @@ Cell.propTypes = {
modifyLocalColumnWidth: PropTypes.func,
};
-export default Cell;
+export default DropTarget('sfMetadataRecordHeaderCell', dropTarget, dropCollect)(
+ DragSource('sfMetadataRecordHeaderCell', dragSource, dragCollect)(Cell)
+);
diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/index.js b/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/index.js
index 86be6fe943..7ccb9342e9 100644
--- a/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/index.js
+++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/records-header/index.js
@@ -1,5 +1,6 @@
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
+import { DropTarget } from 'react-dnd';
import {
HEADER_HEIGHT_TYPE,
isEmptyObject,
@@ -12,6 +13,7 @@ import { getFrozenColumns } from '../../../../../utils/table-utils';
import { isFrozen, recalculateColumnMetricsByResizeColumn } from '../../../../../utils/column-utils';
import { GRID_HEADER_DEFAULT_HEIGHT, GRID_HEADER_DOUBLE_HEIGHT } from '../../../../../constants';
import InsertColumn from './insert-column';
+import html5DragDropContext from '../../../../../../../pages/wiki2/wiki-nav/html5DragDropContext';
const RecordsHeader = ({
isGroupView,
@@ -28,6 +30,7 @@ const RecordsHeader = ({
selectNoneRecords,
selectAllRecords,
modifyColumnWidth: modifyColumnWidthAPI,
+ modifyColumnOrder: modifyColumnOrderAPI,
...props
}) => {
const [resizingColumnMetrics, setResizingColumnMetrics] = useState(null);
@@ -74,8 +77,13 @@ const RecordsHeader = ({
modifyColumnWidthAPI && modifyColumnWidthAPI(column, newWidth);
}, [modifyColumnWidthAPI]);
+ const modifyColumnOrder = useCallback((source, target) => {
+ modifyColumnOrderAPI && modifyColumnOrderAPI(source.key, target.key);
+ }, [modifyColumnOrderAPI]);
+
const frozenColumns = getFrozenColumns(columnMetrics.columns);
const displayColumns = columnMetrics.columns.slice(colOverScanStartIdx, colOverScanEndIdx);
+ const frozenColumnsWidth = frozenColumns.reduce((total, c) => total + c.width, 0);
return (
@@ -104,9 +112,11 @@ const RecordsHeader = ({
column={column}
style={style}
isLastFrozenCell={isLastFrozenCell}
+ frozenColumnsWidth={frozenColumnsWidth}
isHideTriangle={isHideTriangle}
modifyLocalColumnWidth={modifyLocalColumnWidth}
modifyColumnWidth={modifyColumnWidth}
+ onMove={modifyColumnOrder}
{...props}
/>
);
@@ -121,8 +131,10 @@ const RecordsHeader = ({
groupOffsetLeft={groupOffsetLeft}
height={height}
column={column}
+ frozenColumnsWidth={frozenColumnsWidth}
modifyLocalColumnWidth={modifyLocalColumnWidth}
modifyColumnWidth={modifyColumnWidth}
+ onMove={modifyColumnOrder}
{...props}
/>
);
@@ -131,7 +143,6 @@ const RecordsHeader = ({
);
-
};
RecordsHeader.propTypes = {
@@ -150,4 +161,9 @@ RecordsHeader.propTypes = {
selectAllRecords: PropTypes.func,
};
-export default RecordsHeader;
+const DndRecordHeaderContainer = DropTarget('sfMetadataRecordHeaderCell', {}, connect => ({
+ connectDropTarget: connect.dropTarget()
+}))(RecordsHeader);
+
+export default html5DragDropContext(DndRecordHeaderContainer);
+
diff --git a/frontend/src/metadata/metadata-view/components/view-toolbar/index.js b/frontend/src/metadata/metadata-view/components/view-toolbar/index.js
index 06ed020859..af33c9e939 100644
--- a/frontend/src/metadata/metadata-view/components/view-toolbar/index.js
+++ b/frontend/src/metadata/metadata-view/components/view-toolbar/index.js
@@ -39,6 +39,10 @@ const ViewToolBar = ({ viewId }) => {
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_HIDDEN_COLUMNS, hiddenColumns);
}, []);
+ const modifyColumnOrder = useCallback((sourceColumnKey, targetColumnKey) => {
+ window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_COLUMN_ORDER, sourceColumnKey, targetColumnKey);
+ }, []);
+
const viewChange = useCallback((view) => {
setView(view);
}, []);
@@ -109,6 +113,7 @@ const ViewToolBar = ({ viewId }) => {
columns={viewColumns.slice(1)}
hiddenColumns={view.hidden_columns || []}
modifyHiddenColumns={modifyHiddenColumns}
+ modifyColumnOrder={modifyColumnOrder}
/>
diff --git a/frontend/src/metadata/metadata-view/constants/event-bus-type.js b/frontend/src/metadata/metadata-view/constants/event-bus-type.js
index 927de118c4..7df837c39f 100644
--- a/frontend/src/metadata/metadata-view/constants/event-bus-type.js
+++ b/frontend/src/metadata/metadata-view/constants/event-bus-type.js
@@ -42,6 +42,9 @@ export const EVENT_BUS_TYPE = {
// change
VIEW_CHANGED: 'view_changed',
+ // column
+ MODIFY_COLUMN_ORDER: 'modify_column_order',
+
// data status
SAVING: 'saving',
SAVED: 'saved',
diff --git a/frontend/src/metadata/metadata-view/context.js b/frontend/src/metadata/metadata-view/context.js
index d68b3d7511..37db751a9c 100644
--- a/frontend/src/metadata/metadata-view/context.js
+++ b/frontend/src/metadata/metadata-view/context.js
@@ -125,6 +125,11 @@ class Context {
return true;
};
+ canModifyColumnOrder = () => {
+ if (this.permission === 'r') return false;
+ return true;
+ };
+
canModifyView = (view) => {
if (this.permission === 'r') return false;
return true;
diff --git a/frontend/src/metadata/metadata-view/model/metadata/view.js b/frontend/src/metadata/metadata-view/model/metadata/view.js
index 12fa61ea04..ed4846628a 100644
--- a/frontend/src/metadata/metadata-view/model/metadata/view.js
+++ b/frontend/src/metadata/metadata-view/model/metadata/view.js
@@ -1,4 +1,4 @@
-import { VIEW_NOT_DISPLAY_COLUMN_KEYS } from '../../_basic';
+import { getColumnByKey, VIEW_NOT_DISPLAY_COLUMN_KEYS } from '../../_basic';
class View {
constructor(object, columns) {
@@ -25,7 +25,23 @@ class View {
// columns
this.available_columns = columns || [];
- this.columns = this.available_columns.filter(column => !VIEW_NOT_DISPLAY_COLUMN_KEYS.includes(column.key));
+ this.display_available_columns = this.available_columns.filter(column => !VIEW_NOT_DISPLAY_COLUMN_KEYS.includes(column.key));
+ this.columns = this.display_available_columns;
+
+ // order
+ let columnsKeys = object.columns_keys || [];
+ if (columnsKeys.length === 0) {
+ this.columns_keys = this.display_available_columns.map(c => c.key);
+ } else {
+ let columns = columnsKeys.map(key => getColumnByKey(this.display_available_columns, key)).filter(c => c);
+ this.display_available_columns.forEach(column => {
+ if (!getColumnByKey(columns, column.key)) {
+ columns.push(column);
+ }
+ });
+ this.columns_keys = columns.map(c => c.key);
+ this.columns = columns;
+ }
}
}
diff --git a/frontend/src/metadata/metadata-view/store/index.js b/frontend/src/metadata/metadata-view/store/index.js
index b5296d4f86..2023be2846 100644
--- a/frontend/src/metadata/metadata-view/store/index.js
+++ b/frontend/src/metadata/metadata-view/store/index.js
@@ -431,6 +431,19 @@ class Store {
this.applyOperation(operation);
};
+ modifyColumnOrder = (sourceColumnKey, targetColumnKey) => {
+ const type = OPERATION_TYPE.MODIFY_COLUMN_ORDER;
+ const { columns_keys } = this.data.view;
+ const targetColumnIndex = columns_keys.indexOf(targetColumnKey);
+ let newColumnsKeys = columns_keys.slice(0);
+ newColumnsKeys = newColumnsKeys.filter(key => key !== sourceColumnKey);
+ newColumnsKeys.splice(targetColumnIndex, 0, sourceColumnKey);
+ const operation = this.createOperation({
+ type, repo_id: this.repoId, view_id: this.viewId, new_columns_keys: newColumnsKeys, old_columns_keys: columns_keys
+ });
+ this.applyOperation(operation);
+ };
+
}
export default Store;
diff --git a/frontend/src/metadata/metadata-view/store/operations/apply.js b/frontend/src/metadata/metadata-view/store/operations/apply.js
index 87988f24ce..80e843038a 100644
--- a/frontend/src/metadata/metadata-view/store/operations/apply.js
+++ b/frontend/src/metadata/metadata-view/store/operations/apply.js
@@ -171,6 +171,11 @@ export default function apply(data, operation) {
data.view = new View(data.view, data.columns);
return data;
}
+ case OPERATION_TYPE.MODIFY_COLUMN_ORDER: {
+ const { new_columns_keys } = operation;
+ data.view = new View({ ...data.view, columns_keys: new_columns_keys }, data.columns);
+ return data;
+ }
default: {
return data;
}
diff --git a/frontend/src/metadata/metadata-view/store/operations/constants.js b/frontend/src/metadata/metadata-view/store/operations/constants.js
index d79a80895c..fea2cfa805 100644
--- a/frontend/src/metadata/metadata-view/store/operations/constants.js
+++ b/frontend/src/metadata/metadata-view/store/operations/constants.js
@@ -16,6 +16,7 @@ export const OPERATION_TYPE = {
RENAME_COLUMN: 'rename_column',
MODIFY_COLUMN_DATA: 'modify_column_data',
MODIFY_COLUMN_WIDTH: 'modify_column_width',
+ MODIFY_COLUMN_ORDER: 'modify_column_order',
};
export const OPERATION_ATTRIBUTES = {
@@ -33,7 +34,8 @@ export const OPERATION_ATTRIBUTES = {
[OPERATION_TYPE.RENAME_COLUMN]: ['repo_id', 'column_key', 'new_name', 'old_name'],
[OPERATION_TYPE.MODIFY_COLUMN_DATA]: ['repo_id', 'column_key', 'new_data', 'old_data'],
[OPERATION_TYPE.DELETE_COLUMN]: ['repo_id', 'column_key', 'column'],
- [OPERATION_TYPE.MODIFY_COLUMN_WIDTH]: ['repo_id', 'column_key', 'new_width', 'old_width'],
+ [OPERATION_TYPE.MODIFY_COLUMN_WIDTH]: ['column_key', 'new_width', 'old_width'],
+ [OPERATION_TYPE.MODIFY_COLUMN_ORDER]: ['repo_id', 'view_id', 'new_columns_keys', 'old_columns_keys'],
};
export const UNDO_OPERATION_TYPE = [
@@ -61,6 +63,7 @@ export const NEED_APPLY_AFTER_SERVER_OPERATION = [
OPERATION_TYPE.RENAME_COLUMN,
OPERATION_TYPE.MODIFY_COLUMN_DATA,
OPERATION_TYPE.MODIFY_COLUMN_WIDTH,
+ OPERATION_TYPE.MODIFY_COLUMN_ORDER,
];
export const VIEW_OPERATION = [
@@ -76,4 +79,5 @@ export const COLUMN_OPERATION = [
OPERATION_TYPE.RENAME_COLUMN,
OPERATION_TYPE.MODIFY_COLUMN_DATA,
OPERATION_TYPE.MODIFY_COLUMN_WIDTH,
+ OPERATION_TYPE.MODIFY_COLUMN_ORDER,
];
diff --git a/frontend/src/metadata/metadata-view/store/server-operator.js b/frontend/src/metadata/metadata-view/store/server-operator.js
index d608eed35c..4659b68cce 100644
--- a/frontend/src/metadata/metadata-view/store/server-operator.js
+++ b/frontend/src/metadata/metadata-view/store/server-operator.js
@@ -97,6 +97,15 @@ class ServerOperator {
}
break;
}
+ case OPERATION_TYPE.MODIFY_COLUMN_ORDER: {
+ const { repo_id, view_id, new_columns_keys } = operation;
+ window.sfMetadataContext.modifyView(repo_id, view_id, { columns_keys: new_columns_keys }).then(res => {
+ callback({ operation });
+ }).catch(error => {
+ callback({ error: gettext('Failed to modify property order') });
+ });
+ break;
+ }
case OPERATION_TYPE.MODIFY_FILTERS: {
const { repo_id, view_id, filter_conjunction, filters } = operation;
window.sfMetadataContext.modifyView(repo_id, view_id, { filters, filter_conjunction }).then(res => {