我有一个React与JS ES6的任务,我在过去两周一直试图完成。示例和代码位于本文的底部。
我的目标是制作一个“动态可排序的网格”。
我在这里阅读了一些材料,Google搜索并尝试了React Sortable (HOC),react-sortablejs,React Sortable等许多其他库。我试图重写一个库,但没有成功。
在这种情况下,您会使用哪个库,或者在这种情况下您可以建议我做什么?
在输入中,我有一个来自API的JSON:
{
"items":[
{
"sub_items":[
{
"id":1,
"name":"Item 1"
}
],
"type":"Type 1"
},
{
"sub_items":[
{
"id":2,
"name":"Item 2"
},
{
"id":3,
"name":"Item 3"
}
],
"type":"Type 2"
},
{
"sub_items":[
{
"id":4,
"name":"Item 4"
}
],
"type":"Type 3"
},
{
"sub_items":[
{
"id":5,
"name":"Item 5"
},
{
"id":6,
"name":"Item 6"
},
{
"id":7,
"name":"Item 7"
}
],
"type":"Type 4"
}
]
}
JSON保存在React状态,然后在render()
方法中访问。
我的代码:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
App.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// import { SortableItem, swapArrayPositions } from 'react-sort-list';
// import {SortableContainer, SortableElement} from 'react-sortable-hoc';
import { Container, Row, Col, Form, Nav, NavItem } from 'reactstrap';
import NewItem from "./NewItem";
import './App.css';
class App extends Component {
constructor(props){
super(props);
this.state = {
draggable: true,
items: [
{
"sub_items": [
{
"id": 1,
"name": "Item 1"
}
],
"type": "Type 1"
},
{
"sub_items": [
{
"id": 2,
"name": "Item 2"
},
{
"id": 3,
"name": "Item 3"
}
],
"type": "Type 2"
},
{
"sub_items": [
{
"id": 4,
"name": "Item 4"
}
],
"type": "Type 3"
},
{
"sub_items": [
{
"id": 5,
"name": "Item 5"
},
{
"id": 6,
"name": "Item 6"
},
{
"id": 7,
"name": "Item 7"
}
],
"type": "Type 4"
}
]
}
}
dragStart( event ){
var c = event.target.closest(".td");
var p = c.closest(".tr");
event.dataTransfer.setData("text", c.id);
c.style.width = "150px";
if ( p.children.length !== 3 ){
p.classList.remove("full_row")
}
}
dragEnter(event){
}
dragEnd(event){
var data = event.dataTransfer.getData("text");
var p = document.getElementById(data);
var p = event.target.closest(".tr");
if ( p.children.length === 0 ){
try{
p.remove();
}catch( err ){
console.log( err )
}
}else if ( p.children.length === 1 ){
p.classList.remove("full_row")
}else if ( p.children.length === 2 ){
p.classList.remove("full_row")
for ( var i=0, item; item=p.children[i]; i++ ){
item.classList.remove("row_1");
item.classList.remove("row_3");
item.classList.add("row_2");
}
}else if ( p.children.length === 3 ){
for ( var i=0, item; item=p.children[i]; i++ ){
item.classList.remove("row_1");
item.classList.remove("row_2");
item.classList.add("row_3");
}
}
event.target.style.width = null;
var empty_table_rows = document.getElementsByClassName("tr");
for (var i=0, item; item=empty_table_rows[i]; i++ ){
if ( item.children.length === 0 ){
try{
item.remove();
}catch( err ){
console.log( err );
}
}else{
switch ( item.children.length ){
case 1:
item.children[0].classList.remove("row_3");
item.children[0].classList.remove("row_2");
item.children[0].classList.add("row_1");
break;
case 2:
for (var j=0, child; child=item.children[j]; j++ ){
child.classList.remove("row_1");
child.classList.remove("row_3");
child.classList.add("row_2");
}
break;
case 3:
for (var j=0, child; child=item.children[j]; j++ ){
child.classList.remove("row_1");
child.classList.remove("row_2");
child.classList.add("row_3");
}
break;
}
}
}
var new_rows = document.getElementsByClassName("tr_for_drop");
for ( var i=0; i < new_rows.length; i++ ){
if ( new_rows[i].children.length === 1 || new_rows[i].children.length === 0 ){
new_rows[i].remove();
}
}
var new_items = document.getElementsByClassName("new_item");
for ( var i=0; i < new_items.length; i++ ){
var k = new_itms[i].parentNode;
var m = k.closest(".tr");
k.remove();
if ( m.children.length === 0 ){
m.remove();
}
}
}
dragOver( event ){
event.preventDefault();
event.target.closest(".td").classList.remove("row_1");
event.target.closest(".td").classList.remove("row_2");
event.target.closest(".td").classList.add("row_3");
}
tableRowDragOver( event ){
var _this = this;
console.log("TABLE ROW DRAG OVER")
var table_row = event.target.closest(".tr");
var child_number = table_row.children.length;
console.log(child_number);
switch( child_number ){
case 1:
var d = document.createElement("div");
ReactDOM.render(<NewItem
draggable={ _this.state.draggable }
dragStart={ _this.dragStart.bind( _this ) }
dragEnd={ _this.dragEnd.bind( _this ) }
dragOver={ _this.dragOver.bind( _this ) }
dragEnter={ _this.dragEnter.bind( _this ) }
/>, d)
table_row.appendChild(d)
}
}
drop( event ){
var trigger = event.target.closest(".tr").classList.contains("full_row");
if ( trigger ){
event.preventDefault();
return 0;
}else{
event.preventDefault();
var data = event.dataTransfer.getData("text");
var t = document.getElementById( data );
var p = event.target.closest(".tr");
p.appendChild(t);
if (p.children.length === 3){
p.classList.add("full_row")
for ( var i=0, item; item=p.children[i]; i++ ){
item.classList.remove("row_1");
item.classList.remove("row_2");
item.classList.add("row_3");
}
}else if ( p.children.length === 2 ){
for ( var i=0, item; item=p.children[i]; i++ ){
item.classList.remove("row_1");
item.classList.remove("row_3");
item.classList.add("row_2");
}
}
}
var empty_table_rows = document.getElementsByClassName("tr");
for (var i=0, item; item=empty_table_rows[i]; i++ ){
if ( item.children.length === 0 ){
try{
item.remove()
}catch( err ){
console.log( err )
}
}else{
switch ( item.children.length ){
case 1:
item.children[0].classList.remove("row_3");
item.children[0].classList.remove("row_2");
item.children[0].classList.add("row_1");
break;
case 2:
for (var j=0, child; child=item.children[j]; j++ ){
child.classList.remove("row_1");
child.classList.remove("row_3");
child.classList.add("row_2");
}
break;
case 3:
for (var j=0, child; child=item.children[j]; j++ ){
child.classList.remove("row_1");
child.classList.remove("row_2");
child.classList.add("row_3");
}
break;
}
}
}
var new_rows = document.getElementsByClassName("tr_for_drop");
for ( var i=0; i < new_rows.length; i++ ){
if ( new_rows[i].children.length === 1 || new_rows[i].children.length === 0 ){
new_rows[i].remove();
}
}
var new_items = document.getElementsByClassName("new_item");
for ( var i=0; i < new_items.length; i++ ){
var item = new_items[i];
var ch = item.closest(".tr_for_drop").children;
console.log(item.closest(".tr_for_drop"))
if ( ch.length === 1 ){
// item.closest(".tr_for_drop").remove();
}else{
item.closest(".tr_for_drop").classList.add("real_row");
item.closest(".tr_for_drop").classList.remove("tr_for_drop");
item.remove();
}
}
return false;
}
render() {
const Items = (data) => {
console.log(data.items.length)
switch (data.items.length) {
case 1:
return (
<div onDragOver={this.tableRowDragOver.bind(this)}
onDrop={this.drop.bind(this)}
className="tr real_row">
{
data.items.map((item, item_index) => {
return (
<div key={item_index}
onDragStart={this.dragStart.bind(this)}
onDragEnd={this.dragEnd.bind(this)}
onDragOver={this.dragOver.bind(this)}
onDragEnter={this.dragEnter.bind(this)}
draggable={this.state.draggable}
id={"item" + item.id}
className="disable_select td real_item item_cell row_1 ui-sortable itemParent"
data-name={item.name}
data-parent-row="1"
data-col={item_index + 1}>
<div className="command_navigate" style={{
opacity: "0.5",
position: "absolute",
transform: "rotateZ(90deg)",
left: "10px",
top: "10px"
}}>
<i className="ion-ios-more"/>
<i style={{marginTop: "-5px", position: "absolute", left: "0"}}
className="ion-ios-more"/>
</div>
<div data-type="draft_item" className="item_container disable_select">
{item.name}
</div>
</div>
)
})
}
</div>
);
case 2:
return (
<div onDragOver={this.tableRowDragOver.bind(this)}
onDrop={this.drop.bind(this)}
className="tr real_row">
{
data.items.map((item, item_index) => {
return (
<div key={item_index}
onDragStart={this.dragStart.bind(this)}
onDragEnd={this.dragEnd.bind(this)}
onDragOver={this.dragOver.bind(this)}
onDragEnter={this.dragEnter.bind(this)}
draggable={this.state.draggable}
id={"item_" + item.id}
className="disable_select td real_item item_cell row_2 ui-sortable itemParent"
data-id={item.id}
data-name={item.name}
data-col={item_index + 1}>
<div className="item_navigate" style={{
opacity: "0.5",
position: "absolute",
transform: "rotateZ(90deg)",
left: "10px",
top: "10px"
}}>
<i className="ion-ios-more"/>
<i style={{marginTop: "-5px", position: "absolute", left: "0"}}
className="ion-ios-more"/>
</div>
<div data-type="draft_item" className="item_container">
{item.name}
</div>
</div>
)
})
}
</div>
);
case 3:
return (
<div onDragOver={this.tableRowDragOver.bind(this)}
onDrop={this.drop.bind(this)}
className="tr real_row full_row">
{
data.items.map((item, item_index) => {
return (
<div key={item_index}
onDragStart={this.dragStart.bind(this)}
onDragEnd={this.dragEnd.bind(this)}
onDragOver={this.dragOver.bind(this)}
onDragEnter={this.dragEnter.bind(this)}
draggable={this.state.draggable}
id={"item_" + item.id}
className="disable_select td real_item item_cell row_3 ui-sortable itemParent" data-id={item.id}
data-name={item.name}
data-parent-row="1"
data-col={item_index + 1}>
<div className="item_navigate" style={{
opacity: "0.5",
position: "absolute",
transform: "rotateZ(90deg)",
left: "10px",
top: "10px"
}}>
<i className="ion-ios-more"/>
<i style={{marginTop: "-5px", position: "absolute", left: "0"}}
className="ion-ios-more"/>
</div>
<div data-type="draft_item" className="item_container">
{item.name}
</div>
</div>
)
})
}
</div>
)
}
};
return (
<div>
<div className="table ui-sortable">
<div className="tbody">
{
this.state.items.map((item, index) => {
return (
<Items key={ index }
row={ index + 1 }
length={ item.length }
items={ item.sub_items }/>
)
})
}
</div>
</div>
<div className="row_1 item_creator">
Add new item
</div>
</div>
)
}
}
export default App;
NewItem.js
import React, { Component } from 'react';
import './App.css';
export default class NewItem extends Component{
render(){
return(
<div draggable={ this.props.draggable }
onDragStart={ this.props.dragStart }
onDragEnd={ this.props.dragEnd }
onDragOver={ this.props.dragOver }
onDragEnter={ this.props.dragEnter }
className="td new_item">
<div className="item_navigate" style={{ opacity: "0.5", position: "absolute", transform: "rotateZ(90deg)", left: "10px", top: "10px"}}>
<i className="ion-ios-more" />
<i style={{ marginTop: "-5px", position: "absolute", left: "0"}} className="ion-ios-more" />
</div>
<div data-type="draft_item" className="item_container">
DROP HERE
</div>
</div>
)
}
}
App.css
.tr{
margin: 10px 0;
width: 100%;
float: left;
}
.td{
cursor: pointer;
position: relative;
float: left;
border-radius: 5px;
}
.row_1{
width: 465px;
}
.item_creator{
text-align: center;
float: left;
width: 465px;
padding: 10px 0;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
cursor: pointer;
border:dashed 1px grey;
}
.row_2{
float: left;
width: 230px;
margin-right: 10px;
}
.row_2:nth-child(2){
margin-right: 0;
}
.row_3{
float: left;
width: 150px !important;
margin-right: 10px;
max-height: 40px;
overflow: hidden;
white-space: pre-wrap;
text-overflow: ellipsis;
line-height: 1.7;
box-shadow: 0 0 10px -2px rgba(42, 42, 42, 0.3);
}
.row_3:nth-child(3){
margin-right: 0;
}
.item_container{
background: #fff;
padding: 10px 15px;
border-radius: 5px;
text-align: center;
box-shadow: 0 0 10px -2px rgba(42, 42, 42, 0.3);
}
当我得到第2项并将它放在两个实例之间的行上(在随机行之间,或者当我必须将它放在顶行(在第1行的第一行上)时)。排序后项目3 必须将其宽度更改为块的100%(我将类从_row_3_更改为_row_1_)。 第1项的属性为width: 100%
,您可以看到上面的结果。在操作结束时,所有带有标签&#34; DROP HERE&#34;必须删除。
示例2:
如果我拖动第4项并尝试删除(将元素拖到另一条线上)它在2行元素的行上,则必须将它们的宽度更改为~50%(_row_2_ class)直到我删除拖动的元素。此线上的所有元素必须为~33%宽度。在操作结束时,所有带有标签&#34; DROP HERE&#34;必须删除。
此外,如果我想从包含3个项目的行中获取项目并将其放在另一行中,则元素必须根据项目的数量更改其大小。
我如何处理行列之间的切换元素(从一行上的一列到另一行上的另一列)?
行至少包含1个元素,最多包含3个元素。 线条数量是动态的,因此是无限的。