mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-12-27 05:00:48 +08:00
4.5 KiB
4.5 KiB
Drag & Drag
Set draggable
and write bunch of codes to make drag & drop work.
<n-tree
block-node
checkable
draggable
:data="data"
:checked-keys="checkedKeys"
:expanded-keys="expandedKeys"
@drop="handleDrop"
@checked-keys-change="handleCheckedKeysChange"
@expanded-keys-change="handleExpandedKeysChange"
/>
function createData (level = 4, baseKey = '') {
if (!level) return undefined
return Array
.apply(null, { length: 6 - level })
.map((_, index) => {
const key = '' + baseKey + level + index
return {
label: createLabel(level),
key,
children: createData(level - 1, key)
}
})
}
function createLabel (level) {
if (level === 4) return 'Out of Tao, One is born'
if (level === 3) return 'Out of One, Two'
if (level === 2) return 'Out of Two, Three'
if (level === 1) return 'Out of Three, the created universe'
}
function dropIsValid ({
dragNode,
node
}) {
/** drop on itselft */
if (dragNode.key === node.key) return false
/** shouldn't drop parent to its child */
const childKeys = []
const dropNodeInside = children => {
if (!children) return false
return children.reduce((result, child) => {
return result || child.key === node.key || dropNodeInside(child.children)
}, false)
}
return !dropNodeInside(dragNode.children)
}
function findSiblingsAndIndex (node, nodes) {
if (!nodes) return [null, null]
for (let i = 0; i < nodes.length; ++i) {
const siblingNode = nodes[i]
if (siblingNode.key === node.key) return [nodes, i]
const [siblings, index] = findSiblingsAndIndex(node, siblingNode.children)
if (siblings) return [siblings, index]
}
return [null, null]
}
function applyDrop ({
dragNode,
node,
dropPosition
}) {
const parent = findParent(node)
if (dropPosition === 'center') {
const parent = dragNode.parent
const index = parent.children.findIndex(child => child.key === dragNode.key)
if (~index) {
parent.children.splice(index, 1)
if (!parent.children.length) {
parent.children = null
}
}
if (Array.isArray(node.children)) {
if (dropPosition === 'center') {
node.children.push(dragNode)
} else {
node.children.unshift(dragNode)
}
} else {
node.children = [dragNode]
}
dragNode.parent = node
} else if (dropPosition === 'top' || dropPosition === 'bottom') {
const dragNodeIndex = parent.children.findIndex(child => child.key === dragNode.key)
if (~dragNodeIndex) {
parent.children.splice(dragNodeIndex, 1)
if (!parent.children.length) {
parent.children = null
}
}
let nodeIndex = parent.children.findIndex(child => child.key === node.key)
if (dropPosition === 'bottom') nodeIndex += 1
if (~nodeIndex) {
parent.children.splice(nodeIndex, 0, dragNode)
if (!parent.children.length) {
parent.children = null
}
}
}
}
/**
* You may wonder why the demo is so complicated.
* I did take both Element UI like & Antd like's API paradigms into consideration.
* For providing more ability to control the component,
* Antd's paradigm is better.
* Although it is harder to get started with.
* What's more, the time complexity of the demo can be optimized,
* but I'm too lazy to optimize it.
*/
export default {
data () {
return {
data: createData(),
expandedKeys: [],
checkedKeys: []
}
},
methods: {
handleExpandedKeysChange (expandedKeys) {
this.expandedKeys = expandedKeys
},
handleCheckedKeysChange (checkedKeys) {
this.checkedKeys = checkedKeys
},
handleDrop ({
node,
dragNode,
dropPosition
}) {
const valid = dropIsValid({ node, dragNode })
if (!valid) return
const data = this.data
const [dragNodeSiblings, dragNodeIndex] = findSiblingsAndIndex(dragNode, data)
dragNodeSiblings.splice(dragNodeIndex, 1)
if (dropPosition === 'center') {
if (node.children) {
node.children.unshift(dragNode)
} else {
node.children = [dragNode]
}
} else if (dropPosition === 'top') {
const [nodeSiblings, nodeIndex] = findSiblingsAndIndex(node, data)
nodeSiblings.splice(nodeIndex, 0, dragNode)
} else if (dropPosition === 'bottom') {
const [nodeSiblings, nodeIndex] = findSiblingsAndIndex(node, data)
nodeSiblings.splice(nodeIndex + 1, 0, dragNode)
}
this.data = Array.from(data)
}
}
}