mirror of
https://github.com/haiwen/seahub.git
synced 2025-10-21 19:00:12 +00:00
Fix drag and move field in kanban view and card view (#8280)
* 01 fix error typo * 02 fix drag and move hidden fields * 03 optimize codes
This commit is contained in:
@@ -133,7 +133,7 @@ class DirPath extends React.Component {
|
||||
role={children ? 'button' : null}
|
||||
onClick={children ? this.handleRefresh : () => {}}
|
||||
>
|
||||
<MetadataViewName id={viewId} />
|
||||
{viewId && <MetadataViewName id={viewId} />}
|
||||
</span>
|
||||
{children && (
|
||||
<>
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import Switch from '../../../../components/switch';
|
||||
import Icon from '../../../../components/icon';
|
||||
|
||||
function FieldItem({ field, isCollapsed, onToggleField, onMoveField, fieldIconConfig }) {
|
||||
function FieldItem({ field, index, isCollapsed, onToggleField, onMoveField, fieldIconConfig, updateDragOverKey, dragOverColumnKey, draggingColumnIndex, updateDraggingKey }) {
|
||||
let enteredCounter = 0;
|
||||
const fieldItemRef = useRef(null);
|
||||
const [isItemDropTipShow, setDropTipShow] = useState(false);
|
||||
@@ -19,15 +20,17 @@ function FieldItem({ field, isCollapsed, onToggleField, onMoveField, fieldIconCo
|
||||
e.stopPropagation();
|
||||
e.dataTransfer.setDragImage(fieldItemRef.current, 10, 10);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('application/sf-metadata-filed-display-setting', field.key);
|
||||
e.dataTransfer.setData('application/sf-metadata-field-display-setting', field.key);
|
||||
updateDraggingKey(field.key);
|
||||
};
|
||||
|
||||
const onTableDragEnter = (e) => {
|
||||
const onDragEnter = (e) => {
|
||||
e.stopPropagation();
|
||||
enteredCounter++;
|
||||
if (enteredCounter !== 0 && !isItemDropTipShow) {
|
||||
setDropTipShow(true);
|
||||
}
|
||||
updateDragOverKey(field.key);
|
||||
};
|
||||
|
||||
const onDragOver = (e) => {
|
||||
@@ -37,6 +40,7 @@ function FieldItem({ field, isCollapsed, onToggleField, onMoveField, fieldIconCo
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
updateDragOverKey(field.key);
|
||||
};
|
||||
|
||||
const onDragLeave = (e) => {
|
||||
@@ -45,36 +49,45 @@ function FieldItem({ field, isCollapsed, onToggleField, onMoveField, fieldIconCo
|
||||
if (enteredCounter === 0) {
|
||||
setDropTipShow(false);
|
||||
}
|
||||
updateDragOverKey(null);
|
||||
};
|
||||
|
||||
const onDrop = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setDropTipShow(false);
|
||||
const droppedColumnKey = e.dataTransfer.getData('application/sf-metadata-filed-display-setting');
|
||||
const droppedColumnKey = e.dataTransfer.getData('application/sf-metadata-field-display-setting');
|
||||
if (droppedColumnKey === field.key) return;
|
||||
onMoveField(droppedColumnKey, field.key);
|
||||
updateDragOverKey(null);
|
||||
updateDraggingKey(null);
|
||||
};
|
||||
|
||||
const placeholder = () => {
|
||||
return (
|
||||
<div className="sf-metadata-filed-display-setting-switch">
|
||||
<div className="sf-metadata-field-display-setting-switch">
|
||||
<Icon symbol={fieldIconConfig[field.type]} />
|
||||
<span className="text-truncate">{field.name}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const isOver = (dragOverColumnKey === field.key);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={fieldItemRef}
|
||||
className={`sf-metadata-filed-display-setting-item-container ${isCollapsed ? 'd-none' : ''}`}
|
||||
className={classNames('sf-metadata-field-display-setting-item-container',
|
||||
{ 'd-none': isCollapsed },
|
||||
{ 'hide-column-can-drop-top': isOver && draggingColumnIndex >= index },
|
||||
{ 'hide-column-can-drop': isOver && draggingColumnIndex < index },
|
||||
)}
|
||||
onDrop={onDrop}
|
||||
onDragEnter={onTableDragEnter}
|
||||
onDragEnter={onDragEnter}
|
||||
onDragOver={onDragOver}
|
||||
onDragLeave={onDragLeave}
|
||||
>
|
||||
<div className="sf-metadata-filed-display-setting-dragbar" draggable="true" onDragStart={onDragStart}>
|
||||
<div className="sf-metadata-field-display-setting-dragbar" draggable="true" onDragStart={onDragStart}>
|
||||
<Icon symbol="drag" />
|
||||
</div>
|
||||
<Switch
|
||||
@@ -89,10 +102,15 @@ function FieldItem({ field, isCollapsed, onToggleField, onMoveField, fieldIconCo
|
||||
|
||||
FieldItem.propTypes = {
|
||||
isCollapsed: PropTypes.bool,
|
||||
index: PropTypes.number.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
fieldIconConfig: PropTypes.object,
|
||||
onToggleField: PropTypes.func.isRequired,
|
||||
onMoveField: PropTypes.func.isRequired,
|
||||
updateDragOverKey: PropTypes.func.isRequired,
|
||||
dragOverColumnKey: PropTypes.string,
|
||||
draggingColumnIndex: PropTypes.number,
|
||||
updateDraggingKey: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default FieldItem;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
.sf-metadata-filed-display-setting .sf-metadata-filed-display-setting-header .sf-metadata-filed-display-toggle-btn {
|
||||
.sf-metadata-field-display-setting .sf-metadata-field-display-setting-header .sf-metadata-field-display-toggle-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
@@ -9,20 +9,20 @@
|
||||
transition: transform .3s cubic-bezier(.645,.045,.355,1);
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting .sf-metadata-filed-display-setting-header .sf-metadata-filed-display-toggle-btn:hover {
|
||||
.sf-metadata-field-display-setting .sf-metadata-field-display-setting-header .sf-metadata-field-display-toggle-btn:hover {
|
||||
border-radius: 3px;
|
||||
background-color: var(--bs-hover-bg);
|
||||
color: var(--bs-icon-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting .sf-metadata-filed-display-setting-banner .show-all-button {
|
||||
.sf-metadata-field-display-setting .sf-metadata-field-display-setting-banner .show-all-button {
|
||||
color: #FF8000;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting .sf-metadata-filed-display-setting-body .sf-metadata-filed-display-setting-item-container {
|
||||
.sf-metadata-field-display-setting .sf-metadata-field-display-setting-body .sf-metadata-field-display-setting-item-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
@@ -31,21 +31,21 @@
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting .sf-metadata-filed-display-setting-body .sf-metadata-filed-display-setting-item-container:hover {
|
||||
.sf-metadata-field-display-setting .sf-metadata-field-display-setting-body .sf-metadata-field-display-setting-item-container:hover {
|
||||
background-color: var(--bs-nav-hover-bg);
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting .sf-metadata-filed-display-setting-body .sf-metadata-filed-display-setting-dragbar {
|
||||
.sf-metadata-field-display-setting .sf-metadata-field-display-setting-body .sf-metadata-field-display-setting-dragbar {
|
||||
color: #c2c2c2;
|
||||
cursor: grab;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting .sf-metadata-filed-display-setting-body .sf-metadata-filed-display-setting-dragbar .seafile-multicolor-icon-drag {
|
||||
.sf-metadata-field-display-setting .sf-metadata-field-display-setting-body .sf-metadata-field-display-setting-dragbar .seafile-multicolor-icon-drag {
|
||||
vertical-align: -0.15em;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting-item-container .sf-metadata-switch.custom-switch {
|
||||
.sf-metadata-field-display-setting-item-container .sf-metadata-switch.custom-switch {
|
||||
width: 100%;
|
||||
padding-left: 0;
|
||||
display: inline-flex;
|
||||
@@ -53,7 +53,7 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting-item-container .sf-metadata-switch .custom-switch .seafile-multicolor-icon {
|
||||
.sf-metadata-field-display-setting-item-container .sf-metadata-switch .custom-switch .seafile-multicolor-icon {
|
||||
fill: #666;
|
||||
transform: scale(0.8);
|
||||
font-size: 16px;
|
||||
@@ -61,25 +61,43 @@
|
||||
vertical-align: -0.15em;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting-item-container .sf-metadata-switch .custom-switch .custom-switch-description {
|
||||
.sf-metadata-field-display-setting-item-container .sf-metadata-switch .custom-switch .custom-switch-description {
|
||||
margin-left: 0;
|
||||
padding-right: 5px;
|
||||
flex: 1;
|
||||
transition: .3s color;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting-item-container .sf-metadata-switch .custom-switch .custom-switch-indicator {
|
||||
.sf-metadata-field-display-setting-item-container .sf-metadata-switch .custom-switch .custom-switch-indicator {
|
||||
width: 22px;
|
||||
height: 12px;
|
||||
border-radius: 6px;
|
||||
margin-right: .25rem;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting-item-container .sf-metadata-switch .custom-switch .custom-switch-indicator::before {
|
||||
.sf-metadata-field-display-setting-item-container .sf-metadata-switch .custom-switch .custom-switch-indicator::before {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.sf-metadata-filed-display-setting-item-container .sf-metadata-switch .custom-switch-input:checked~.custom-switch-indicator:before {
|
||||
.sf-metadata-field-display-setting-item-container .sf-metadata-switch .custom-switch-input:checked~.custom-switch-indicator:before {
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.sf-metadata-field-display-setting-item-container.hide-column-can-drop::after,
|
||||
.sf-metadata-field-display-setting-item-container.hide-column-can-drop-top::before {
|
||||
content: '';
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
background: #666;
|
||||
}
|
||||
|
||||
.sf-metadata-field-display-setting-item-container.hide-column-can-drop::after {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.sf-metadata-field-display-setting-item-container.hide-column-can-drop-top::before {
|
||||
top: 0;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useRef, useState, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Label } from 'reactstrap';
|
||||
import classnames from 'classnames';
|
||||
@@ -15,6 +15,18 @@ const DURATION = 300;
|
||||
const FieldDisplaySettings = ({ fieldIconConfig, fields, textProperties, onToggleField, onMoveField, onToggleFieldsVisibility }) => {
|
||||
const nodeRef = useRef(null);
|
||||
const [isCollapsed, setCollapsed] = useState(true);
|
||||
const [dragOverColumnKey, setDragOverCellKey] = useState(null);
|
||||
const [draggingColumnKey, setDraggingCellKey] = useState(null);
|
||||
|
||||
const updateDragOverKey = useCallback((cellKey) => {
|
||||
if (cellKey === dragOverColumnKey) return;
|
||||
setDragOverCellKey(cellKey);
|
||||
}, [dragOverColumnKey]);
|
||||
|
||||
const updateDraggingKey = useCallback((cellKey) => {
|
||||
if (cellKey === draggingColumnKey) return;
|
||||
setDraggingCellKey(cellKey);
|
||||
}, [draggingColumnKey]);
|
||||
|
||||
const expandAllFields = () => {
|
||||
setCollapsed(!isCollapsed);
|
||||
@@ -31,39 +43,45 @@ const FieldDisplaySettings = ({ fieldIconConfig, fields, textProperties, onToggl
|
||||
exited: { opacity: 0, height: 0 },
|
||||
};
|
||||
const fieldAllShown = fields.every(field => field.shown);
|
||||
const draggingColumnIndex = draggingColumnKey ? fields.findIndex(f => f.key === draggingColumnKey) : -1;
|
||||
|
||||
return (
|
||||
<div className="sf-metadata-filed-display-setting">
|
||||
<div className="sf-metadata-field-display-setting">
|
||||
<div
|
||||
className="sf-metadata-filed-display-setting-header d-flex align-items-center justify-content-between"
|
||||
className="sf-metadata-field-display-setting-header d-flex align-items-center justify-content-between"
|
||||
onClick={expandAllFields}
|
||||
role="button"
|
||||
aria-label={isCollapsed ? gettext('Expand') : gettext('Collapse')}
|
||||
>
|
||||
<Label className="mb-0">{textProperties.titleValue}</Label>
|
||||
<div className="sf-metadata-filed-display-toggle-btn">
|
||||
<div className="sf-metadata-field-display-toggle-btn">
|
||||
<i aria-hidden="true" className={classnames('sf3-font sf3-font-down', { 'rotate-270': isCollapsed })}></i>
|
||||
</div>
|
||||
</div>
|
||||
<Transition nodeRef={nodeRef} in={!isCollapsed} timeout={DURATION}>
|
||||
{state => (
|
||||
<div className="sf-metadata-filed-display-setting-wrapper" ref={nodeRef} style={{ ...defaultStyle, ...transitionStyles[state] }}>
|
||||
<div className={`sf-metadata-filed-display-setting-banner ${isCollapsed ? 'd-none' : 'd-flex'} align-items-center justify-content-between h-5 mt-2 mb-2`}>
|
||||
<div className="sf-metadata-field-display-setting-wrapper" ref={nodeRef} style={{ ...defaultStyle, ...transitionStyles[state] }}>
|
||||
<div className={`sf-metadata-field-display-setting-banner ${isCollapsed ? 'd-none' : 'd-flex'} align-items-center justify-content-between h-5 mt-2 mb-2`}>
|
||||
<Label className="mb-0">{textProperties.bannerValue}</Label>
|
||||
<span className="show-all-button" onClick={() => onToggleFieldsVisibility(!fieldAllShown)}>
|
||||
{fieldAllShown ? textProperties.hideValue : textProperties.showValue}
|
||||
</span>
|
||||
</div>
|
||||
<div className="sf-metadata-filed-display-setting-body">
|
||||
<div className="sf-metadata-field-display-setting-body">
|
||||
{fields.map((field, index) => {
|
||||
return (
|
||||
<FieldItem
|
||||
key={`${field.key}-${index}`}
|
||||
field={field}
|
||||
index={index}
|
||||
fieldIconConfig={fieldIconConfig}
|
||||
isCollapsed={isCollapsed}
|
||||
onToggleField={onToggleField}
|
||||
onMoveField={onMoveField}
|
||||
updateDragOverKey={updateDragOverKey}
|
||||
updateDraggingKey={updateDraggingKey}
|
||||
dragOverColumnKey={dragOverColumnKey}
|
||||
draggingColumnIndex={draggingColumnIndex}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@@ -4,14 +4,13 @@ import { useMetadata } from '../hooks';
|
||||
|
||||
const MetadataViewName = ({ id }) => {
|
||||
const { idViewMap } = useMetadata();
|
||||
if (!id) return null;
|
||||
const view = idViewMap[id];
|
||||
if (!view) return null;
|
||||
return (<>{view.name}</>);
|
||||
};
|
||||
|
||||
MetadataViewName.propTypes = {
|
||||
id: PropTypes.string,
|
||||
id: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default MetadataViewName;
|
||||
|
@@ -22,10 +22,10 @@ const HideColumnItem = ({
|
||||
const ref = useRef(null);
|
||||
|
||||
const onDragStart = useCallback((event) => {
|
||||
const dragData = JSON.stringify({ type: 'sf-metadata-filed-display-setting', column_key: column.key });
|
||||
const dragData = JSON.stringify({ type: 'sf-metadata-field-display-setting', column_key: column.key });
|
||||
event.dataTransfer.setDragImage(ref.current, 10, 10);
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.dataTransfer.setData('application/drag-sf-metadata-filed-display-setting', dragData);
|
||||
event.dataTransfer.setData('application/drag-sf-metadata-field-display-setting', dragData);
|
||||
updateDraggingKey(column.key);
|
||||
}, [column, updateDraggingKey]);
|
||||
|
||||
@@ -48,10 +48,10 @@ const HideColumnItem = ({
|
||||
|
||||
const onDrop = useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
let dragData = event.dataTransfer.getData('application/drag-sf-metadata-filed-display-setting');
|
||||
let dragData = event.dataTransfer.getData('application/drag-sf-metadata-field-display-setting');
|
||||
if (!dragData) return false;
|
||||
dragData = JSON.parse(dragData);
|
||||
if (dragData.type !== 'sf-metadata-filed-display-setting' || !dragData.column_key) return false;
|
||||
if (dragData.type !== 'sf-metadata-field-display-setting' || !dragData.column_key) return false;
|
||||
if (dragData.column_key !== column.key) {
|
||||
onMove && onMove(dragData.column_key, column.key);
|
||||
}
|
||||
|
Reference in New Issue
Block a user