mirror of
https://github.com/haiwen/seahub.git
synced 2025-07-14 07:24:58 +00:00
Feat metadata column order (#6622)
* feat: metdata column order * feat: hidden column support adjust column order * feat: optimzie code --------- Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
parent
645099a405
commit
b32b3fe904
@ -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;
|
||||
|
@ -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,7 +68,27 @@ const HideColumnItem = ({ readOnly, column, isHidden, onChange }) => {
|
||||
}, [readOnly, column, onChange]);
|
||||
|
||||
return (
|
||||
<div className={classNames('hide-column-item', { 'disabled': readOnly })}>
|
||||
<>
|
||||
{connectDropTarget(
|
||||
connectDragPreview(
|
||||
<div
|
||||
className={classNames('hide-column-item', {
|
||||
'disabled': readOnly,
|
||||
'hide-column-can-drop-top': isOver && canDrop && isDragging,
|
||||
'hide-column-can-drop': isOver && canDrop && !isDragging
|
||||
})}
|
||||
onMouseEnter={() => onMouseEnter(columnIndex)}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
{!readOnly && (
|
||||
<>
|
||||
{connectDragSource(
|
||||
<div className="drag-hide-column-handle">
|
||||
<Icon iconName="drag" />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Switch
|
||||
disabled={readOnly}
|
||||
checked={isHidden}
|
||||
@ -26,14 +102,24 @@ const HideColumnItem = ({ readOnly, column, isHidden, onChange }) => {
|
||||
switchClassName="hide-column-item-switch"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
HideColumnItem.propTypes = {
|
||||
readOnly: PropTypes.bool,
|
||||
isHidden: PropTypes.bool,
|
||||
columnIndex: PropTypes.number,
|
||||
currentIndex: PropTypes.number,
|
||||
column: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onMove: PropTypes.func,
|
||||
onMouseEnter: PropTypes.func,
|
||||
onMouseLeave: PropTypes.func,
|
||||
};
|
||||
|
||||
export default HideColumnItem;
|
||||
export default DropTarget('sfMetadataHiddenColumns', dropTarget, dropCollect)(
|
||||
DragSource('sfMetadataHiddenColumns', dragSource, dragCollect)(HideColumnItem)
|
||||
);
|
||||
|
@ -1,26 +1,44 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { DropTarget } from 'react-dnd';
|
||||
import { gettext } from '../../../../utils';
|
||||
import HideColumn from './hide-column';
|
||||
import html5DragDropContext from '../../../../../../pages/wiki2/wiki-nav/html5DragDropContext';
|
||||
|
||||
const HiddenColumns = ({ readOnly, columns, hiddenColumns, onChange, modifyColumnOrder }) => {
|
||||
const [currentIndex, setCurrentIndex] = useState(-1);
|
||||
|
||||
const HiddenColumns = ({ readOnly, columns, hiddenColumns, onChange }) => {
|
||||
const isEmpty = useMemo(() => {
|
||||
if (!Array.isArray(columns) || columns.length === 0) return true;
|
||||
return false;
|
||||
}, [columns]);
|
||||
|
||||
const onMouseEnter = useCallback((columnIndex) => {
|
||||
if (currentIndex === columnIndex) return;
|
||||
setCurrentIndex(columnIndex);
|
||||
}, [currentIndex]);
|
||||
|
||||
const onMouseLeave = useCallback(() => {
|
||||
setCurrentIndex(-1);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={classnames('hide-columns-list', { 'empty-hide-columns-container': isEmpty })}>
|
||||
{isEmpty && <div className="empty-hide-columns-list">{gettext('No properties available to be hidden')}</div>}
|
||||
{!isEmpty && columns.map((column) => {
|
||||
{!isEmpty && columns.map((column, columnIndex) => {
|
||||
return (
|
||||
<HideColumn
|
||||
key={column.key}
|
||||
readOnly={readOnly}
|
||||
columnIndex={columnIndex}
|
||||
currentIndex={currentIndex}
|
||||
isHidden={!hiddenColumns.includes(column.key)}
|
||||
column={column}
|
||||
onChange={onChange}
|
||||
onMove={modifyColumnOrder}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
<div className="sf-metadata-hide-columns-search-container">
|
||||
<SearchInput placeholder={gettext('Search property')} onKeyDown={onKeyDown} onChange={onChangeSearch} autoFocus={true}/>
|
||||
</div>
|
||||
<HiddenColumns readOnly={readOnly} columns={displayColumns} hiddenColumns={hiddenColumns} onChange={hideColumn} />
|
||||
<HiddenColumns readOnly={readOnly} columns={displayColumns} hiddenColumns={hiddenColumns} onChange={hideColumn} modifyColumnOrder={modifyColumnOrder} />
|
||||
{!readOnly && !searchValue && (
|
||||
<div className="sf-metadata-hide-columns-operations">
|
||||
<div className="sf-metadata-hide-columns-operation px-2" onClick={hideAll} aria-label={gettext('Hide all')}>{gettext('Hide all')}</div>
|
||||
@ -125,6 +125,7 @@ HideColumnPopover.propTypes = {
|
||||
columns: PropTypes.array.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
hidePopover: PropTypes.func.isRequired,
|
||||
modifyColumnOrder: PropTypes.func,
|
||||
};
|
||||
|
||||
export default HideColumnPopover;
|
||||
|
@ -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' && (<Gallery />)}
|
||||
|
@ -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 })}
|
||||
</div>
|
||||
@ -746,6 +747,7 @@ Records.propTypes = {
|
||||
deleteColumn: PropTypes.func,
|
||||
modifyColumnData: PropTypes.func,
|
||||
modifyColumnWidth: PropTypes.func,
|
||||
modifyColumnOrder: PropTypes.func,
|
||||
getCopiedRecordsAndColumnsFromRange: PropTypes.func,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,16 +135,22 @@ 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 (
|
||||
<div key={key} className="sf-metadata-record-header-cell">
|
||||
const canModifyColumnOrder = window.sfMetadataContext.canModifyColumnOrder();
|
||||
const cell = (
|
||||
<div
|
||||
className={classnames('sf-metadata-result-table-cell column', { 'table-last--frozen': isLastFrozenCell })}
|
||||
ref={headerCellRef}
|
||||
style={style}
|
||||
id={`sf-metadata-column-${key}`}
|
||||
onClick={() => handleHeaderCellClick(column, frozen)}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
<div className="sf-metadata-result-column-content sf-metadata-record-header-cell-left d-flex align-items-center text-truncate">
|
||||
<span className="mr-2" id={`header-icon-${key}`}>
|
||||
@ -100,9 +173,29 @@ const Cell = ({
|
||||
)}
|
||||
<ResizeColumnHandle onDrag={onDrag} onDragEnd={onDragEnd} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (!canModifyColumnOrder || column.key === PRIVATE_COLUMN_KEY.FILE_NAME) {
|
||||
return (
|
||||
<div key={key} className="sf-metadata-record-header-cell">
|
||||
{cell}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={key} className="sf-metadata-record-header-cell">
|
||||
{connectDropTarget(
|
||||
connectDragPreview(
|
||||
connectDragSource(
|
||||
<div style={{ opacity: isDragging ? 0.2 : 1 }} className={classnames('rdg-can-drop', { 'rdg-dropping': isOver && canDrop })}>
|
||||
{cell}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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)
|
||||
);
|
||||
|
@ -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 (
|
||||
<div className="static-sf-metadata-result-content grid-header" style={{ height: height + 1 }}>
|
||||
@ -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 = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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}
|
||||
/>
|
||||
</div>
|
||||
<div className="sf-metadata-tool-right-operations"></div>
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
];
|
||||
|
@ -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 => {
|
||||
|
Loading…
Reference in New Issue
Block a user