4

我正在尝试使用Material UI的and集成React DnD,并且在拖动时,整个列表显示为拖动的元素。我试图尽我所能理解这些例子,这就是我所拥有的ListListItem

import React, { Component, PropTypes } from 'react';
import { Random } from 'meteor/random';
import LocalizedComponent from '/client/components/LocalizedComponent';
// MUI
import { List, ListItem } from 'material-ui/List';
// ---
import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
import { findDOMNode } from 'react-dom';

import HTML5Backend from 'react-dnd-html5-backend';


const itemSource = {
  beginDrag(props) {
    return {
      id: props.id,
      index: props.index
    };
  },
};

const itemTarget = {
  hover(props, monitor, component) {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return;
    }

    // Determine rectangle on screen
    const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();

    // Get vertical middle
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;

    // Determine mouse position
    const clientOffset = monitor.getClientOffset();

    // Get pixels to the top
    const hoverClientY = clientOffset.y - hoverBoundingRect.top;

    // Only perform the move when the mouse has crossed half of the items height
    // When dragging downwards, only move when the cursor is below 50%
    // When dragging upwards, only move when the cursor is above 50%

    // Dragging downwards
    if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
      return;
    }

    // Dragging upwards
    if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
      return;
    }

    // Time to actually perform the action
    props.onMoveItem(dragIndex, hoverIndex);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex;
  },
};


class SortableListComponent extends Component {

  render() {
    const { children, onMoveItem } = this.props;
    let index = 0;

    return (
      <List>
        { React.Children.map(children, child => React.cloneElement(child, {
          id: Random.id(),
          index: index++,
          onMoveItem: onMoveItem
        })) }
      </List>
    );
  }
}

SortableListComponent.propTypes = {
  onMoveItem: PropTypes.func.isRequired
};


class SortableListItemComponent extends Component {

  render() {
    const {
      id,
      index,
      isDragging,
      connectDragSource,
      connectDropTarget,
      onMoveItem,
      ...other
    } = this.props;
    const opacity = 1; // isDragging ? 0 : 1;

    return connectDragSource(connectDropTarget(
      <div style={{ opacity }}>
        <ListItem { ...other } disabled={ isDragging } />
      </div>
    ));
  }
}
SortableListItemComponent.propTypes = {
  connectDragSource: PropTypes.func.isRequired,
  connectDropTarget: PropTypes.func.isRequired,
  id: PropTypes.any.isRequired,
  index: PropTypes.number.isRequired,
  isDragging: PropTypes.bool.isRequired,
  onMoveItem: PropTypes.func.isRequired,
};


export const SortableList = DragDropContext(HTML5Backend)(SortableListComponent);

export const SortableListItem = DropTarget('SortableListItem', itemTarget, connect => ({
  connectDropTarget: connect.dropTarget(),
}))(DragSource('SortableListItem', itemSource, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  isDragging: monitor.isDragging(),
}))(SortableListItemComponent));

基本上,我用ListSortableList代替ListItemSortableListItem这是我在拖动时看到的

在此处输入图像描述

我究竟做错了什么?

编辑

例如,这里是一个示例用法

<SortableList>
  { actions.map((action, index) => (
    <SortableListItem id={ action.name } key={ index }
      primaryText={ (index + 1) + '. ' + action.name }
      onTouchTap={ this.handleActionEdit.bind(this, index) }
    />
  )) }
</SortableList>

或者

<SortableList>
  { actions.map((action, index) => (
    <SortableListItem id={ action.name } key={ action.name }
      primaryText={ (index + 1) + '. ' + action.name }
      onTouchTap={ this.handleActionEdit.bind(this, index) }
    />
  )) }
</SortableList>

等等

它不会改变任何事情。

4

3 回答 3

2

我也遇到了类似的问题,发现这在 chrome 上不能正常工作。有关类似问题,请参阅https://github.com/react-dnd/react-dnd/issues/832。用户的以下评论帮助我解决了这个问题。

@kaiomagalhaes在我的情况下,我遇到了这个问题,因为行的一个子元素(单元格内容)实际上具有大于行高的高度,但被可见性隐藏:隐藏在css中。所以 dragSource 有行的宽度和隐藏控件的高度。

我希望你会发现这很有帮助。

于 2018-05-04T11:20:14.007 回答
1

我还尝试<List>使用 React-DND 制作可排序的 Material-UI,但遇到了同样的问题,但快照是我的整个页面,而不仅仅是列表!

仍在努力解决它,但我确实注意到如果您使用常规<div>而不是<ListItem />. 所以我担心这可能是 Material-UI 的怪癖。最简单的解决方案可能只是避免<ListItem>.

我确实想出了一个解决方法,如果我找到更好的答案,我会编辑我的答案:

用法

import SortableList from './SortableList';

<SortableList
    items={[
        {id: 1, text: 'one'},
        {id: 2, text: 'two'},
        {id: 3, text: 'three'},
        {id: 4, text: 'four'},
    ]}
    onChange={items => {
        console.log(JSON.stringify(items));
        // flux action, etc. goes here
    }}
    listItemProps={(item, index) => {
        return {
            primaryText: item.name,
            hoverColor: 'green',
            // pass desired props to <ListItem> here
        };
    }}
/>

排序列表.jsx

import React, { Component, PropTypes } from 'react';
import update from 'react-addons-update';

import List, { ListItem } from 'material-ui/List';

import { findDOMNode } from 'react-dom';
import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';

const SortableListTypes = {
    ITEM: 'ITEM',
};

class SortableListItem extends Component {
    render() {
        const { connectDragSource, connectDragPreview, connectDropTarget, ...rest } = this.props;
        return (
            <ListItem
                id={this.props.id + '/' + this.props.index}
                key={this.props.id + '/' + this.props.index}
                ref={instance => {
                    if (!instance) {
                        return;
                    }
                    const node = findDOMNode(instance);
                    connectDragSource(node);
                    connectDropTarget(node);

                    const greatGrandChild = node.childNodes[0].childNodes[0].childNodes[0];
                    connectDragPreview(greatGrandChild);
                }}
                {...rest}
            />
        );
    }
}

SortableListItem = DropTarget(
    SortableListTypes.ITEM,
    {
        hover: (props, monitor, component) => {
            const dragIndex = monitor.getItem().index;
            const hoverIndex = props.index;

            if (dragIndex === hoverIndex) {
                return;
            }

            const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
            const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
            const clientOffset = monitor.getClientOffset();
            const hoverClientY = clientOffset.y - hoverBoundingRect.top;
            if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
                return;
            }
            if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
                return;
            }

            props.moveItem(dragIndex, hoverIndex);
            monitor.getItem().index = hoverIndex;
        },
        drop: props => props.dropHandler()
    },
    (connect, monitor) => {
        return {
            connectDropTarget: connect.dropTarget(),
        }
    }
)(DragSource(
    SortableListTypes.ITEM,
    {
        beginDrag: (props, monitor, component) => {
            return {
                id: props.id,
                index: props.index,
            }
        }
    },
    (connect, monitor) => {
        return {
            connectDragSource: connect.dragSource(),
            connectDragPreview: connect.dragPreview(),
        }
    }
)(SortableListItem));

class SortableList extends Component {
    constructor(props) {
        super(props);

        this.state = {
            items: _.clone(props.items),
        };

        this.moveItem = this.moveItem.bind(this);
        this.dropHandler = this.dropHandler.bind(this);
    }

    componentWillReceiveProps(nextProps) {
        this.setState({
            items: _.clone(nextProps.items),
        });
    }

    moveItem(fromIndex, toIndex) {
        const draggedItem = this.state.items[fromIndex];

        this.setState(update(this.state, {
            items: {
                $splice: [
                    [fromIndex, 1],
                    [toIndex, 0, draggedItem],
                ],
            },
        }));
    }

    dropHandler() {
        this.props.onChange(_.clone(this.state.items));
    }

    render() {
        return (
            <List>
                {this.state.items.map((item, index) => (
                    <SortableListItem
                        key={item.id}
                        index={index}
                        moveItem={this.moveItem}
                        dropHandler={this.dropHandler}
                        {...this.props.listItemProps(item, index)}
                    />
                ))}
            </List>
        )
    }
}

export default DragDropContext(HTML5Backend)(SortableList);

排序列表项.jsx

import React, { Component, PropTypes } from 'react';

import { ListItem } from 'material-ui/List';

import { findDOMNode } from 'react-dom';
import { DragSource, DropTarget } from 'react-dnd';

const SortableListTypes = {
    ITEM: 'ITEM',
};

class SortableListItem extends Component {
    render() {
        const { connectDragSource, connectDragPreview, connectDropTarget, ...rest } = this.props;
        return (
            <ListItem
                id={this.props.id + '/' + this.props.index}
                key={this.props.id + '/' + this.props.index}
                ref={instance => {
                    if (!instance) {
                        return;
                    }
                    const node = findDOMNode(instance);
                    connectDragSource(node);
                    connectDropTarget(node);

                    const greatGrandChild = node.childNodes[0].childNodes[0].childNodes[0];
                    connectDragPreview(greatGrandChild);
                }}
                {...rest}
            />
        );
    }
}

export default DropTarget(
    SortableListTypes.ITEM,
    {
        hover: (props, monitor, component) => {
            const dragIndex = monitor.getItem().index;
            const hoverIndex = props.index;

            if (dragIndex === hoverIndex) {
                return;
            }

            const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
            const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
            const clientOffset = monitor.getClientOffset();
            const hoverClientY = clientOffset.y - hoverBoundingRect.top;
            if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
                return;
            }
            if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
                return;
            }

            props.moveItem(dragIndex, hoverIndex);
            monitor.getItem().index = hoverIndex;
        },
        drop: props => props.dropHandler()
    },
    (connect, monitor) => {
        return {
            connectDropTarget: connect.dropTarget(),
        }
    }
)(DragSource(
    SortableListTypes.ITEM,
    {
        beginDrag: (props, monitor, component) => {
            return {
                id: props.id,
                index: props.index,
            }
        }
    },
    (connect, monitor) => {
        return {
            connectDragSource: connect.dragSource(),
            connectDragPreview: connect.dragPreview(),
        }
    }
)(SortableListItem));

绝不是一个完美的解决方案,但希望它有所帮助。如果您的项目没有id属性,则需要编辑keywithinSortableListItem的属性SortableList.render

于 2017-03-17T22:09:02.930 回答
1

问题可能是您如何将您的id和传递key到您的子组件中。id使用 React dnd生成随机最终会出现问题。您应该做的是,在您的情况下,您应该在数据中拥有每个项目的唯一 ID children。一个例子在这里:https ://github.com/react-dnd/react-dnd/blob/master/examples/04%20Sortable/Simple/Container.js#L17

{ React.Children.map(children, child => React.cloneElement(child, {
    id: child.id,
    key: child.id,
    index: index++,
    onMoveItem: onMoveItem
})) }
于 2017-02-14T21:32:05.487 回答