mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-05 08:53:14 +00:00
Grid view support multi-select by drag (#6420)
* grid view support multiple selection by drag * fix conflict with multiple selection by keyboard * improve selection experience and code format * fix bug - select failed when file name include '...'
This commit is contained in:
@@ -65,6 +65,7 @@ const propTypes = {
|
|||||||
isAllItemSelected: PropTypes.bool.isRequired,
|
isAllItemSelected: PropTypes.bool.isRequired,
|
||||||
onAllItemSelected: PropTypes.func.isRequired,
|
onAllItemSelected: PropTypes.func.isRequired,
|
||||||
selectedDirentList: PropTypes.array.isRequired,
|
selectedDirentList: PropTypes.array.isRequired,
|
||||||
|
onSelectedDirentListUpdate: PropTypes.func.isRequired,
|
||||||
onItemsMove: PropTypes.func.isRequired,
|
onItemsMove: PropTypes.func.isRequired,
|
||||||
onItemsCopy: PropTypes.func.isRequired,
|
onItemsCopy: PropTypes.func.isRequired,
|
||||||
onItemsDelete: PropTypes.func.isRequired,
|
onItemsDelete: PropTypes.func.isRequired,
|
||||||
@@ -254,6 +255,7 @@ class DirColumnView extends React.Component {
|
|||||||
direntList={this.props.direntList}
|
direntList={this.props.direntList}
|
||||||
fullDirentList={this.props.fullDirentList}
|
fullDirentList={this.props.fullDirentList}
|
||||||
selectedDirentList={this.props.selectedDirentList}
|
selectedDirentList={this.props.selectedDirentList}
|
||||||
|
onSelectedDirentListUpdate={this.props.onSelectedDirentListUpdate}
|
||||||
onAddFile={this.props.onAddFile}
|
onAddFile={this.props.onAddFile}
|
||||||
onItemClick={this.props.onItemClick}
|
onItemClick={this.props.onItemClick}
|
||||||
onItemDelete={this.props.onItemDelete}
|
onItemDelete={this.props.onItemDelete}
|
||||||
|
@@ -12,6 +12,7 @@ const propTypes = {
|
|||||||
updateUsedRepoTags: PropTypes.func.isRequired,
|
updateUsedRepoTags: PropTypes.func.isRequired,
|
||||||
direntList: PropTypes.array.isRequired,
|
direntList: PropTypes.array.isRequired,
|
||||||
selectedDirentList: PropTypes.array.isRequired,
|
selectedDirentList: PropTypes.array.isRequired,
|
||||||
|
onSelectedDirentListUpdate: PropTypes.func.isRequired,
|
||||||
onItemClick: PropTypes.func.isRequired,
|
onItemClick: PropTypes.func.isRequired,
|
||||||
onGridItemClick: PropTypes.func,
|
onGridItemClick: PropTypes.func,
|
||||||
onAddFile: PropTypes.func.isRequired,
|
onAddFile: PropTypes.func.isRequired,
|
||||||
@@ -73,6 +74,7 @@ class DirGridView extends React.Component {
|
|||||||
direntList={this.props.direntList}
|
direntList={this.props.direntList}
|
||||||
fullDirentList={this.props.fullDirentList}
|
fullDirentList={this.props.fullDirentList}
|
||||||
selectedDirentList={this.props.selectedDirentList}
|
selectedDirentList={this.props.selectedDirentList}
|
||||||
|
onSelectedDirentListUpdate={this.props.onSelectedDirentListUpdate}
|
||||||
onAddFile={this.props.onAddFile}
|
onAddFile={this.props.onAddFile}
|
||||||
onItemClick={this.props.onItemClick}
|
onItemClick={this.props.onItemClick}
|
||||||
onItemDelete={this.props.onItemDelete}
|
onItemDelete={this.props.onItemDelete}
|
||||||
|
@@ -77,16 +77,16 @@ class DirentGridItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dirent === activeDirent && !event.metaKey && !event.ctrlKey) {
|
if (dirent === activeDirent && !event.metaKey && !event.ctrlKey) {
|
||||||
this.handleDoubleClick(dirent);
|
this.handleDoubleClick(dirent, event);
|
||||||
} else {
|
} else {
|
||||||
this.props.onGridItemClick(dirent, event);
|
this.props.onGridItemClick(dirent, event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDoubleClick = (dirent) => {
|
handleDoubleClick = (dirent, event) => {
|
||||||
if (Utils.imageCheck(dirent.name)) {
|
if (Utils.imageCheck(dirent.name)) {
|
||||||
this.props.showImagePopup(dirent);
|
this.props.showImagePopup(dirent);
|
||||||
this.props.onGridItemClick(null);
|
this.props.onGridItemClick(null, event);
|
||||||
} else {
|
} else {
|
||||||
this.props.onItemClick(dirent);
|
this.props.onItemClick(dirent);
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ class DirentGridItem extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleDoubleClick(dirent);
|
this.handleDoubleClick(dirent, e);
|
||||||
};
|
};
|
||||||
|
|
||||||
onGridItemDragStart = (e) => {
|
onGridItemDragStart = (e) => {
|
||||||
|
@@ -31,6 +31,7 @@ const propTypes = {
|
|||||||
direntList: PropTypes.array.isRequired,
|
direntList: PropTypes.array.isRequired,
|
||||||
fullDirentList: PropTypes.array,
|
fullDirentList: PropTypes.array,
|
||||||
selectedDirentList: PropTypes.array.isRequired,
|
selectedDirentList: PropTypes.array.isRequired,
|
||||||
|
onSelectedDirentListUpdate: PropTypes.func.isRequired,
|
||||||
onAddFile: PropTypes.func,
|
onAddFile: PropTypes.func,
|
||||||
onItemDelete: PropTypes.func,
|
onItemDelete: PropTypes.func,
|
||||||
onItemCopy: PropTypes.func.isRequired,
|
onItemCopy: PropTypes.func.isRequired,
|
||||||
@@ -86,10 +87,160 @@ class DirentGridView extends React.Component {
|
|||||||
isGridItemFreezed: false,
|
isGridItemFreezed: false,
|
||||||
activeDirent: null,
|
activeDirent: null,
|
||||||
downloadItems: [],
|
downloadItems: [],
|
||||||
|
|
||||||
|
startPoint: { x: 0, y: 0 },
|
||||||
|
endPoint: { x: 0, y: 0 },
|
||||||
|
selectedItemsList: [],
|
||||||
|
isSelecting: false,
|
||||||
|
isMouseDown: false,
|
||||||
|
autoScrollInterval: null,
|
||||||
};
|
};
|
||||||
|
this.containerRef = React.createRef();
|
||||||
this.isRepoOwner = props.currentRepoInfo.owner_email === username;
|
this.isRepoOwner = props.currentRepoInfo.owner_email === username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
window.addEventListener('mouseup', this.onGlobalMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('mouseup', this.onGlobalMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGridContainerMouseDown = (event) => {
|
||||||
|
if (event.button === 2) {
|
||||||
|
return;
|
||||||
|
} else if (event.button === 0) {
|
||||||
|
hideMenu();
|
||||||
|
this.props.onGridItemClick(null);
|
||||||
|
if (event.target.closest('img') || event.target.closest('div.grid-file-name')) return;
|
||||||
|
|
||||||
|
const containerBounds = this.containerRef.current.getBoundingClientRect();
|
||||||
|
this.setState({
|
||||||
|
startPoint: { x: event.clientX - containerBounds.left, y: event.clientY - containerBounds.top },
|
||||||
|
endPoint: { x: event.clientX - containerBounds.left, y: event.clientY - containerBounds.top },
|
||||||
|
selectedItemsList: [],
|
||||||
|
isSelecting: false,
|
||||||
|
isMouseDown: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onSelectMouseMove = (e) => {
|
||||||
|
if (!this.state.isMouseDown) return;
|
||||||
|
|
||||||
|
const containerBounds = this.containerRef.current.getBoundingClientRect();
|
||||||
|
const endPoint = { x: e.clientX - containerBounds.left, y: e.clientY - containerBounds.top };
|
||||||
|
|
||||||
|
// Constrain endPoint within the container bounds
|
||||||
|
endPoint.x = Math.max(0, Math.min(endPoint.x, containerBounds.width));
|
||||||
|
endPoint.y = Math.max(0, Math.min(endPoint.y, containerBounds.height));
|
||||||
|
|
||||||
|
// Check if the mouse has moved a certain distance to start selection, prevents accidental selections
|
||||||
|
const distance = Math.sqrt(
|
||||||
|
Math.pow(endPoint.x - this.state.startPoint.x, 2) +
|
||||||
|
Math.pow(endPoint.y - this.state.startPoint.y, 2)
|
||||||
|
);
|
||||||
|
if (distance > 5) {
|
||||||
|
this.setState({
|
||||||
|
isSelecting: true,
|
||||||
|
endPoint: endPoint,
|
||||||
|
}, () => {
|
||||||
|
this.determineSelectedItems();
|
||||||
|
this.autoScroll(e.clientY);
|
||||||
|
|
||||||
|
const selectedItemNames = new Set(this.state.selectedItemsList.map(item => item.lastChild.lastChild.title));
|
||||||
|
const filteredDirentList = this.props.direntList
|
||||||
|
.filter(dirent => selectedItemNames.has(dirent.name))
|
||||||
|
.map(dirent => ({ ...dirent, isSelected: true }));
|
||||||
|
|
||||||
|
this.props.onSelectedDirentListUpdate(filteredDirentList);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onGlobalMouseUp = () => {
|
||||||
|
if (!this.state.isMouseDown) return;
|
||||||
|
clearInterval(this.state.autoScrollInterval);
|
||||||
|
this.setState({
|
||||||
|
isSelecting: false,
|
||||||
|
isMouseDown: false,
|
||||||
|
autoScrollInterval: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
determineSelectedItems = () => {
|
||||||
|
const { startPoint, endPoint } = this.state;
|
||||||
|
const container = this.containerRef.current;
|
||||||
|
const items = container.querySelectorAll('.grid-item');
|
||||||
|
|
||||||
|
const selectionRect = {
|
||||||
|
left: Math.min(startPoint.x, endPoint.x),
|
||||||
|
top: Math.min(startPoint.y, endPoint.y),
|
||||||
|
right: Math.max(startPoint.x, endPoint.x),
|
||||||
|
bottom: Math.max(startPoint.y, endPoint.y),
|
||||||
|
};
|
||||||
|
|
||||||
|
const newSelectedItemsList = [];
|
||||||
|
|
||||||
|
items.forEach(item => {
|
||||||
|
const bounds = item.getBoundingClientRect();
|
||||||
|
const relativeBounds = {
|
||||||
|
left: bounds.left - container.getBoundingClientRect().left,
|
||||||
|
top: bounds.top - container.getBoundingClientRect().top,
|
||||||
|
right: bounds.right - container.getBoundingClientRect().left,
|
||||||
|
bottom: bounds.bottom - container.getBoundingClientRect().top,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the element is within the selection box's bounds
|
||||||
|
if (relativeBounds.left < selectionRect.right && relativeBounds.right > selectionRect.left &&
|
||||||
|
relativeBounds.top < selectionRect.bottom && relativeBounds.bottom > selectionRect.top) {
|
||||||
|
newSelectedItemsList.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({ selectedItemsList: newSelectedItemsList });
|
||||||
|
};
|
||||||
|
|
||||||
|
autoScroll = (mouseY) => {
|
||||||
|
const container = this.containerRef.current;
|
||||||
|
const containerBounds = container.getBoundingClientRect();
|
||||||
|
const scrollSpeed = 10;
|
||||||
|
const scrollThreshold = 20;
|
||||||
|
|
||||||
|
const updateEndPoint = () => {
|
||||||
|
const endPoint = {
|
||||||
|
x: this.state.endPoint.x,
|
||||||
|
y: mouseY - containerBounds.top + container.scrollTop,
|
||||||
|
};
|
||||||
|
this.setState({ endPoint }, () => {
|
||||||
|
this.determineSelectedItems();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (mouseY < containerBounds.top + scrollThreshold) {
|
||||||
|
// Scroll Up
|
||||||
|
if (!this.state.autoScrollInterval) {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
container.scrollTop -= scrollSpeed;
|
||||||
|
updateEndPoint();
|
||||||
|
}, 50);
|
||||||
|
this.setState({ autoScrollInterval: interval });
|
||||||
|
}
|
||||||
|
} else if (mouseY > containerBounds.bottom - scrollThreshold) {
|
||||||
|
// Scroll Down
|
||||||
|
if (!this.state.autoScrollInterval) {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
container.scrollTop += scrollSpeed;
|
||||||
|
updateEndPoint();
|
||||||
|
}, 50);
|
||||||
|
this.setState({ autoScrollInterval: interval });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearInterval(this.state.autoScrollInterval);
|
||||||
|
this.setState({ autoScrollInterval: null });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onCreateFileToggle = (fileType) => {
|
onCreateFileToggle = (fileType) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isCreateFileDialogShow: !this.state.isCreateFileDialogShow,
|
isCreateFileDialogShow: !this.state.isCreateFileDialogShow,
|
||||||
@@ -444,27 +595,17 @@ class DirentGridView extends React.Component {
|
|||||||
return Utils.checkDuplicatedNameInList(this.props.direntList, newName);
|
return Utils.checkDuplicatedNameInList(this.props.direntList, newName);
|
||||||
};
|
};
|
||||||
|
|
||||||
// common contextmenu handle
|
onGridItemMouseDown = (event) => {
|
||||||
onMouseDown = (event) => {
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
if (event.button === 2) {
|
if (event.button === 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onGridContainerMouseDown = (event) => {
|
gridContainerClick = (event) => {
|
||||||
this.onMouseDown(event);
|
event.stopPropagation();
|
||||||
};
|
|
||||||
|
|
||||||
onGridItemMouseDown = (event) => {
|
|
||||||
this.onMouseDown(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
gridContainerClick = () => {
|
|
||||||
hideMenu();
|
hideMenu();
|
||||||
if (!this.props.isDirentDetailShow) {
|
|
||||||
this.onGridItemClick(null);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onGridContainerContextMenu = (event) => {
|
onGridContainerContextMenu = (event) => {
|
||||||
@@ -509,7 +650,7 @@ class DirentGridView extends React.Component {
|
|||||||
let menuList = Utils.getDirentOperationList(this.isRepoOwner, currentRepoInfo, selectedDirentList[0], true);
|
let menuList = Utils.getDirentOperationList(this.isRepoOwner, currentRepoInfo, selectedDirentList[0], true);
|
||||||
this.handleContextClick(event, GRID_ITEM_CONTEXTMENU_ID, menuList, selectedDirentList[0]);
|
this.handleContextClick(event, GRID_ITEM_CONTEXTMENU_ID, menuList, selectedDirentList[0]);
|
||||||
} else {
|
} else {
|
||||||
this.onDirentClick(null);
|
this.props.onGridItemClick(null);
|
||||||
event.persist();
|
event.persist();
|
||||||
if (!hasCustomPermission('modify')) return;
|
if (!hasCustomPermission('modify')) return;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -577,6 +718,21 @@ class DirentGridView extends React.Component {
|
|||||||
return Utils.getDirentOperationList(isRepoOwner, currentRepoInfo, dirent, isContextmenu);
|
return Utils.getDirentOperationList(isRepoOwner, currentRepoInfo, dirent, isContextmenu);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
renderSelectionBox = () => {
|
||||||
|
const { startPoint, endPoint } = this.state;
|
||||||
|
if (!this.state.isSelecting) return null;
|
||||||
|
const left = Math.min(startPoint.x, endPoint.x);
|
||||||
|
const top = Math.min(startPoint.y, endPoint.y);
|
||||||
|
const width = Math.abs(startPoint.x - endPoint.x);
|
||||||
|
const height = Math.abs(startPoint.y - endPoint.y);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="selection-box"
|
||||||
|
style={{ left, top, width, height }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { direntList, selectedDirentList, path } = this.props;
|
let { direntList, selectedDirentList, path } = this.props;
|
||||||
let dirent = this.state.activeDirent ? this.state.activeDirent : '';
|
let dirent = this.state.activeDirent ? this.state.activeDirent : '';
|
||||||
@@ -588,7 +744,14 @@ class DirentGridView extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ul className="grid-view" onClick={this.gridContainerClick} onContextMenu={this.onGridContainerContextMenu} onMouseDown={this.onGridContainerMouseDown}>
|
<ul
|
||||||
|
className="grid-view"
|
||||||
|
onClick={this.gridContainerClick}
|
||||||
|
onContextMenu={this.onGridContainerContextMenu}
|
||||||
|
onMouseDown={this.onGridContainerMouseDown}
|
||||||
|
onMouseMove={this.onSelectMouseMove}
|
||||||
|
ref={this.containerRef}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
direntList.length !== 0 && direntList.map((dirent, index) => {
|
direntList.length !== 0 && direntList.map((dirent, index) => {
|
||||||
return (
|
return (
|
||||||
@@ -609,6 +772,7 @@ class DirentGridView extends React.Component {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
{this.renderSelectionBox()}
|
||||||
</ul>
|
</ul>
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
id={GRID_ITEM_CONTEXTMENU_ID}
|
id={GRID_ITEM_CONTEXTMENU_ID}
|
||||||
|
@@ -46,6 +46,7 @@ class ViewFileToolbar extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onDropDownMouseMove = (e) => {
|
onDropDownMouseMove = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
if (this.state.isSubMenuShown && e.target && e.target.className === 'dropdown-item') {
|
if (this.state.isSubMenuShown && e.target && e.target.className === 'dropdown-item') {
|
||||||
this.setState({
|
this.setState({
|
||||||
isSubMenuShown: false
|
isSubMenuShown: false
|
||||||
|
@@ -85,3 +85,9 @@
|
|||||||
.grid-drop-show {
|
.grid-drop-show {
|
||||||
background: #f8f8f8;
|
background: #f8f8f8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.selection-box {
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(0, 120, 215, 0.3);
|
||||||
|
border: 1px solid rgba(0, 120, 215, 0.8);
|
||||||
|
}
|
||||||
|
@@ -56,6 +56,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dir-content-main {
|
.dir-content-main {
|
||||||
|
position: relative;
|
||||||
flex: 1 0 74.5%;
|
flex: 1 0 74.5%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@@ -81,6 +81,7 @@ const propTypes = {
|
|||||||
isDirentDetailShow: PropTypes.bool.isRequired,
|
isDirentDetailShow: PropTypes.bool.isRequired,
|
||||||
selectedDirent: PropTypes.object,
|
selectedDirent: PropTypes.object,
|
||||||
selectedDirentList: PropTypes.array.isRequired,
|
selectedDirentList: PropTypes.array.isRequired,
|
||||||
|
onSelectedDirentListUpdate: PropTypes.func.isRequired,
|
||||||
onItemsMove: PropTypes.func.isRequired,
|
onItemsMove: PropTypes.func.isRequired,
|
||||||
onItemsCopy: PropTypes.func.isRequired,
|
onItemsCopy: PropTypes.func.isRequired,
|
||||||
onItemsDelete: PropTypes.func.isRequired,
|
onItemsDelete: PropTypes.func.isRequired,
|
||||||
@@ -305,6 +306,7 @@ class LibContentContainer extends React.Component {
|
|||||||
isAllItemSelected={this.props.isAllDirentSelected}
|
isAllItemSelected={this.props.isAllDirentSelected}
|
||||||
onAllItemSelected={this.props.onAllDirentSelected}
|
onAllItemSelected={this.props.onAllDirentSelected}
|
||||||
selectedDirentList={this.props.selectedDirentList}
|
selectedDirentList={this.props.selectedDirentList}
|
||||||
|
onSelectedDirentListUpdate={this.props.onSelectedDirentListUpdate}
|
||||||
onItemsMove={this.props.onItemsMove}
|
onItemsMove={this.props.onItemsMove}
|
||||||
onItemsCopy={this.props.onItemsCopy}
|
onItemsCopy={this.props.onItemsCopy}
|
||||||
onItemsDelete={this.props.onItemsDelete}
|
onItemsDelete={this.props.onItemsDelete}
|
||||||
|
@@ -1378,6 +1378,18 @@ class LibContentView extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onSelectedDirentListUpdate = (newSelectedDirentList, lastSelectedIndex = null) => {
|
||||||
|
this.setState({
|
||||||
|
direntList: this.state.direntList.map(dirent => {
|
||||||
|
dirent.isSelected = newSelectedDirentList.some(selectedDirent => selectedDirent.name === dirent.name);
|
||||||
|
return dirent;
|
||||||
|
}),
|
||||||
|
isDirentSelected: newSelectedDirentList.length > 0,
|
||||||
|
selectedDirentList: newSelectedDirentList,
|
||||||
|
lastSelectedIndex: lastSelectedIndex,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onItemClick = (dirent) => {
|
onItemClick = (dirent) => {
|
||||||
this.resetSelected();
|
this.resetSelected();
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
@@ -2152,6 +2164,7 @@ class LibContentView extends React.Component {
|
|||||||
isDirentDetailShow={this.state.isDirentDetailShow}
|
isDirentDetailShow={this.state.isDirentDetailShow}
|
||||||
selectedDirent={this.state.selectedDirentList && this.state.selectedDirentList[0]}
|
selectedDirent={this.state.selectedDirentList && this.state.selectedDirentList[0]}
|
||||||
selectedDirentList={this.state.selectedDirentList}
|
selectedDirentList={this.state.selectedDirentList}
|
||||||
|
onSelectedDirentListUpdate={this.onSelectedDirentListUpdate}
|
||||||
onItemsMove={this.onMoveItems}
|
onItemsMove={this.onMoveItems}
|
||||||
onItemsCopy={this.onCopyItems}
|
onItemsCopy={this.onCopyItems}
|
||||||
onItemsDelete={this.onDeleteItems}
|
onItemsDelete={this.onDeleteItems}
|
||||||
|
Reference in New Issue
Block a user