mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-24 12:45:18 +08:00
wip(tree): refactor drag and drop
This commit is contained in:
parent
0a1b65f2a7
commit
cf561be670
@ -132,6 +132,7 @@ const treeProps = {
|
|||||||
onDragleave: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
onDragleave: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
||||||
onDragend: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
onDragend: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
||||||
onDragstart: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
onDragstart: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
||||||
|
onDragover: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
||||||
onDrop: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
onDrop: [Function, Array] as PropType<MaybeArray<(e: DragInfo) => void>>,
|
||||||
// eslint-disable-next-line vue/prop-name-casing
|
// eslint-disable-next-line vue/prop-name-casing
|
||||||
'onUpdate:expandedKeys': [Function, Array] as PropType<
|
'onUpdate:expandedKeys': [Function, Array] as PropType<
|
||||||
@ -257,13 +258,18 @@ export default defineComponent({
|
|||||||
treeMateRef.value.getFlattenedNodes(mergedExpandedKeysRef.value)
|
treeMateRef.value.getFlattenedNodes(mergedExpandedKeysRef.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
const draggingNodeKeyRef = ref<Key | null>(null)
|
|
||||||
const draggingNodeRef = ref<TreeOption | null>(null)
|
|
||||||
const droppingNodeKeyRef = ref<Key | null>(null)
|
|
||||||
const expandTimerIdRef = ref<number | undefined>(undefined)
|
const expandTimerIdRef = ref<number | undefined>(undefined)
|
||||||
const highlightKeysRef = ref<Key[]>([])
|
const highlightKeysRef = ref<Key[]>([])
|
||||||
const loadingKeysRef = ref<Key[]>([])
|
const loadingKeysRef = ref<Key[]>([])
|
||||||
|
|
||||||
|
const draggingNodeRef = ref<TmNode | null>(null)
|
||||||
|
const droppingNodeRef = ref<TmNode | null>(null)
|
||||||
|
const droppingNodeParentRef = computed(() => {
|
||||||
|
const { value: droppingNode } = droppingNodeRef
|
||||||
|
if (droppingNode) return droppingNode.parent
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
watch(toRef(props, 'data'), () => {
|
watch(toRef(props, 'data'), () => {
|
||||||
loadingKeysRef.value = []
|
loadingKeysRef.value = []
|
||||||
expandTimerIdRef.value = undefined
|
expandTimerIdRef.value = undefined
|
||||||
@ -420,14 +426,17 @@ export default defineComponent({
|
|||||||
const { onDragstart } = props
|
const { onDragstart } = props
|
||||||
if (onDragstart) call(onDragstart, info)
|
if (onDragstart) call(onDragstart, info)
|
||||||
}
|
}
|
||||||
|
function doDragOver (info: DragInfo): void {
|
||||||
|
const { onDragover } = props
|
||||||
|
if (onDragover) call(onDragover, info)
|
||||||
|
}
|
||||||
function doDrop (info: DropInfo): void {
|
function doDrop (info: DropInfo): void {
|
||||||
const { onDrop } = props
|
const { onDrop } = props
|
||||||
if (onDrop) call(onDrop, info)
|
if (onDrop) call(onDrop, info)
|
||||||
}
|
}
|
||||||
function resetDragStatus (): void {
|
function resetDragStatus (): void {
|
||||||
draggingNodeKeyRef.value = null
|
|
||||||
draggingNodeRef.value = null
|
draggingNodeRef.value = null
|
||||||
droppingNodeKeyRef.value = null
|
droppingNodeRef.value = null
|
||||||
}
|
}
|
||||||
function handleCheck (node: TmNode, checked: boolean): void {
|
function handleCheck (node: TmNode, checked: boolean): void {
|
||||||
if (props.disabled || node.disabled) return
|
if (props.disabled || node.disabled) return
|
||||||
@ -482,18 +491,22 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Dnd
|
||||||
function handleDragEnter ({ event, node }: InternalDragInfo): void {
|
function handleDragEnter ({ event, node }: InternalDragInfo): void {
|
||||||
// node should be a tmNode
|
// node should be a tmNode
|
||||||
if (!props.draggable || props.disabled || node.disabled) return
|
if (!props.draggable || props.disabled || node.disabled) return
|
||||||
doDragEnter({ event, node: node.rawNode })
|
doDragEnter({ event, node: node.rawNode })
|
||||||
if (!props.expandOnDragenter) return
|
if (!props.expandOnDragenter) return
|
||||||
droppingNodeKeyRef.value = node.key
|
droppingNodeRef.value = node
|
||||||
if (node.key === draggingNodeKeyRef.value) return
|
const { value: draggingNode } = draggingNodeRef
|
||||||
|
if (draggingNode && node.key === draggingNode.key) return
|
||||||
if (!mergedExpandedKeysRef.value.includes(node.key) && !node.isLeaf) {
|
if (!mergedExpandedKeysRef.value.includes(node.key) && !node.isLeaf) {
|
||||||
window.clearTimeout(expandTimerIdRef.value)
|
window.clearTimeout(expandTimerIdRef.value)
|
||||||
const expand = (): void => {
|
const expand = (): void => {
|
||||||
|
const { value: droppingNode } = droppingNodeRef
|
||||||
if (
|
if (
|
||||||
droppingNodeKeyRef.value === node.key &&
|
droppingNode &&
|
||||||
|
droppingNode.key === node.key &&
|
||||||
!mergedExpandedKeysRef.value.includes(node.key)
|
!mergedExpandedKeysRef.value.includes(node.key)
|
||||||
) {
|
) {
|
||||||
doExpandedKeysChange(mergedExpandedKeysRef.value.concat(node.key))
|
doExpandedKeysChange(mergedExpandedKeysRef.value.concat(node.key))
|
||||||
@ -522,7 +535,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
function handleDragLeave ({ event, node }: InternalDragInfo): void {
|
function handleDragLeave ({ event, node }: InternalDragInfo): void {
|
||||||
if (!props.draggable || props.disabled || node.disabled) return
|
if (!props.draggable || props.disabled || node.disabled) return
|
||||||
droppingNodeKeyRef.value = null
|
droppingNodeRef.value = null
|
||||||
doDragLeave({ event, node: node.rawNode })
|
doDragLeave({ event, node: node.rawNode })
|
||||||
}
|
}
|
||||||
function handleDragEnd ({ event, node }: InternalDragInfo): void {
|
function handleDragEnd ({ event, node }: InternalDragInfo): void {
|
||||||
@ -532,10 +545,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
function handleDragStart ({ event, node }: InternalDragInfo): void {
|
function handleDragStart ({ event, node }: InternalDragInfo): void {
|
||||||
if (!props.draggable || props.disabled || node.disabled) return
|
if (!props.draggable || props.disabled || node.disabled) return
|
||||||
draggingNodeKeyRef.value = node.key
|
draggingNodeRef.value = node
|
||||||
draggingNodeRef.value = node.rawNode
|
|
||||||
doDragStart({ event, node: node.rawNode })
|
doDragStart({ event, node: node.rawNode })
|
||||||
}
|
}
|
||||||
|
function handleDragOver ({ event, node }: InternalDragInfo): void {
|
||||||
|
doDragOver({ event, node: node.rawNode })
|
||||||
|
}
|
||||||
function handleDrop ({ event, node, dropPosition }: InternalDropInfo): void {
|
function handleDrop ({ event, node, dropPosition }: InternalDropInfo): void {
|
||||||
if (
|
if (
|
||||||
!props.draggable ||
|
!props.draggable ||
|
||||||
@ -548,7 +563,7 @@ export default defineComponent({
|
|||||||
doDrop({
|
doDrop({
|
||||||
event,
|
event,
|
||||||
node: node.rawNode,
|
node: node.rawNode,
|
||||||
dragNode: draggingNodeRef.value,
|
dragNode: draggingNodeRef.value.rawNode,
|
||||||
dropPosition
|
dropPosition
|
||||||
})
|
})
|
||||||
resetDragStatus()
|
resetDragStatus()
|
||||||
@ -571,12 +586,17 @@ export default defineComponent({
|
|||||||
onLoadRef: toRef(props, 'onLoad'),
|
onLoadRef: toRef(props, 'onLoad'),
|
||||||
draggableRef: toRef(props, 'draggable'),
|
draggableRef: toRef(props, 'draggable'),
|
||||||
checkableRef: toRef(props, 'checkable'),
|
checkableRef: toRef(props, 'checkable'),
|
||||||
|
blockLineRef: toRef(props, 'blockLine'),
|
||||||
|
droppingNodeRef,
|
||||||
|
droppingNodeParentRef,
|
||||||
|
draggingNodeRef,
|
||||||
handleSwitcherClick,
|
handleSwitcherClick,
|
||||||
handleDragEnd,
|
handleDragEnd,
|
||||||
handleDragEnter,
|
handleDragEnter,
|
||||||
handleDragLeave,
|
handleDragLeave,
|
||||||
handleDragStart,
|
handleDragStart,
|
||||||
handleDrop,
|
handleDrop,
|
||||||
|
handleDragOver,
|
||||||
handleSelect,
|
handleSelect,
|
||||||
handleCheck
|
handleCheck
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { h, inject, computed, defineComponent, PropType } from 'vue'
|
import { h, inject, computed, defineComponent, PropType, ref } from 'vue'
|
||||||
import { useMemo } from 'vooks'
|
import { useMemo } from 'vooks'
|
||||||
import NTreeNodeSwitcher from './TreeNodeSwitcher'
|
import NTreeNodeSwitcher from './TreeNodeSwitcher'
|
||||||
import NTreeNodeCheckbox from './TreeNodeCheckbox'
|
import NTreeNodeCheckbox from './TreeNodeCheckbox'
|
||||||
@ -45,20 +45,47 @@ const TreeNode = defineComponent({
|
|||||||
function handleContentClick (e: MouseEvent): void {
|
function handleContentClick (e: MouseEvent): void {
|
||||||
NTree.handleSelect(props.tmNode)
|
NTree.handleSelect(props.tmNode)
|
||||||
}
|
}
|
||||||
function handleDragEnter (e: DragEvent): void {
|
|
||||||
NTree.handleDragEnter({
|
function handleCheck (checked: boolean): void {
|
||||||
event: e,
|
NTree.handleCheck(props.tmNode, checked)
|
||||||
node: props.tmNode
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
// Dnd
|
||||||
|
const pendingPositionRef = ref<'top' | 'center' | 'bottom' | null>(null)
|
||||||
function handleDragStart (e: DragEvent): void {
|
function handleDragStart (e: DragEvent): void {
|
||||||
NTree.handleDragStart({
|
NTree.handleDragStart({
|
||||||
event: e,
|
event: e,
|
||||||
node: props.tmNode
|
node: props.tmNode
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function handleDragLeave (e: DragEvent): void {
|
function handleDragEnter (e: DragEvent): void {
|
||||||
NTree.handleDragLeave({
|
if (
|
||||||
|
e.currentTarget &&
|
||||||
|
e.relatedTarget &&
|
||||||
|
(e.currentTarget as HTMLElement).contains(
|
||||||
|
e.relatedTarget as HTMLElement
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
NTree.handleDragEnter({
|
||||||
|
event: e,
|
||||||
|
node: props.tmNode
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function handleDragOver (e: DragEvent): void {
|
||||||
|
e.preventDefault()
|
||||||
|
const el = e.currentTarget as HTMLElement
|
||||||
|
const elOffsetHeight = el.offsetHeight // dangerous
|
||||||
|
const elClientTop = el.getBoundingClientRect().top
|
||||||
|
const eventOffsetY = e.clientY - elClientTop
|
||||||
|
if (eventOffsetY <= 8) {
|
||||||
|
pendingPositionRef.value = 'top'
|
||||||
|
} else if (eventOffsetY >= elOffsetHeight - 8) {
|
||||||
|
pendingPositionRef.value = 'bottom'
|
||||||
|
} else {
|
||||||
|
pendingPositionRef.value = 'center'
|
||||||
|
}
|
||||||
|
NTree.handleDragOver({
|
||||||
event: e,
|
event: e,
|
||||||
node: props.tmNode
|
node: props.tmNode
|
||||||
})
|
})
|
||||||
@ -69,18 +96,35 @@ const TreeNode = defineComponent({
|
|||||||
node: props.tmNode
|
node: props.tmNode
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function handleDrop (
|
function handleDragLeave (e: DragEvent): void {
|
||||||
e: DragEvent,
|
if (
|
||||||
dropPosition: 'bottom' | 'center' | 'top'
|
e.currentTarget &&
|
||||||
): void {
|
e.relatedTarget &&
|
||||||
NTree.handleDrop({
|
(e.currentTarget as HTMLElement).contains(
|
||||||
|
e.relatedTarget as HTMLElement
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
NTree.handleDragLeave({
|
||||||
event: e,
|
event: e,
|
||||||
node: props.tmNode,
|
node: props.tmNode
|
||||||
dropPosition
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function handleCheck (checked: boolean): void {
|
function handleDrop (e: DragEvent): void {
|
||||||
NTree.handleCheck(props.tmNode, checked)
|
e.preventDefault()
|
||||||
|
if (pendingPositionRef.value !== null) {
|
||||||
|
const dropPosition = ({
|
||||||
|
top: 'top',
|
||||||
|
bottom: 'bottom',
|
||||||
|
center: 'center'
|
||||||
|
} as const)[pendingPositionRef.value]
|
||||||
|
NTree.handleDrop({
|
||||||
|
event: e,
|
||||||
|
node: props.tmNode,
|
||||||
|
dropPosition
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
loading: useMemo(() =>
|
loading: useMemo(() =>
|
||||||
@ -104,10 +148,13 @@ const TreeNode = defineComponent({
|
|||||||
icon: computed(() => props.tmNode.rawNode.icon),
|
icon: computed(() => props.tmNode.rawNode.icon),
|
||||||
checkable: NTree.checkableRef,
|
checkable: NTree.checkableRef,
|
||||||
draggable: NTree.draggableRef,
|
draggable: NTree.draggableRef,
|
||||||
|
blockLine: NTree.blockLineRef,
|
||||||
|
pendingPosition: pendingPositionRef,
|
||||||
handleCheck,
|
handleCheck,
|
||||||
handleDrop,
|
handleDrop,
|
||||||
handleDragStart,
|
handleDragStart,
|
||||||
handleDragEnter,
|
handleDragEnter,
|
||||||
|
handleDragOver,
|
||||||
handleDragEnd,
|
handleDragEnd,
|
||||||
handleDragLeave,
|
handleDragLeave,
|
||||||
handleContentClick,
|
handleContentClick,
|
||||||
@ -115,51 +162,75 @@ const TreeNode = defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
render () {
|
render () {
|
||||||
const { tmNode, clsPrefix, checkable, selected, highlight } = this
|
const {
|
||||||
|
tmNode,
|
||||||
|
clsPrefix,
|
||||||
|
checkable,
|
||||||
|
selected,
|
||||||
|
highlight,
|
||||||
|
draggable,
|
||||||
|
blockLine
|
||||||
|
} = this
|
||||||
|
// drag start not inside
|
||||||
|
// it need to be append to node itself, not wrapper
|
||||||
|
const dragEventHandlers = draggable
|
||||||
|
? {
|
||||||
|
onDragenter: this.handleDragEnter,
|
||||||
|
onDragleave: this.handleDragLeave,
|
||||||
|
onDragend: this.handleDragEnd,
|
||||||
|
onDrop: this.handleDrop
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
return (
|
return (
|
||||||
<li
|
<div
|
||||||
class={[
|
class={`${clsPrefix}-tree-node-wrapper`}
|
||||||
`${clsPrefix}-tree-node`,
|
{...(blockLine ? dragEventHandlers : undefined)}
|
||||||
{
|
|
||||||
[`${clsPrefix}-tree-node--selected`]: selected,
|
|
||||||
[`${clsPrefix}-tree-node--checkable`]: checkable,
|
|
||||||
[`${clsPrefix}-tree-node--hightlight`]: highlight
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
{Array.apply(null, { length: tmNode.level } as any).map(() => (
|
<div
|
||||||
<div class={`${clsPrefix}-tree-node-indent`}></div>
|
class={[
|
||||||
))}
|
`${clsPrefix}-tree-node`,
|
||||||
<NTreeNodeSwitcher
|
{
|
||||||
clsPrefix={clsPrefix}
|
[`${clsPrefix}-tree-node--selected`]: selected,
|
||||||
expanded={this.expanded}
|
[`${clsPrefix}-tree-node--checkable`]: checkable,
|
||||||
loading={this.loading}
|
[`${clsPrefix}-tree-node--hightlight`]: highlight
|
||||||
hide={tmNode.isLeaf}
|
}
|
||||||
onClick={this.handleSwitcherClick}
|
]}
|
||||||
/>
|
draggable={draggable && blockLine}
|
||||||
{checkable ? (
|
onDragstart={
|
||||||
<NTreeNodeCheckbox
|
draggable && blockLine ? this.handleDragStart : undefined
|
||||||
clsPrefix={clsPrefix}
|
}
|
||||||
checked={this.checked}
|
|
||||||
indeterminate={this.indeterminate}
|
|
||||||
onCheck={this.handleCheck}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<NTreeNodeContent
|
|
||||||
clsPrefix={clsPrefix}
|
|
||||||
onClick={this.handleContentClick}
|
|
||||||
onDragenter={this.handleDragEnter}
|
|
||||||
onDragstart={this.handleDragStart}
|
|
||||||
onDragleave={this.handleDragLeave}
|
|
||||||
onDragend={this.handleDragEnd}
|
|
||||||
onDrop={this.handleDrop}
|
|
||||||
>
|
>
|
||||||
{{
|
{Array.apply(null, { length: tmNode.level } as any).map(() => (
|
||||||
default: () => tmNode.rawNode.label
|
<div class={`${clsPrefix}-tree-node-indent`}></div>
|
||||||
}}
|
))}
|
||||||
</NTreeNodeContent>
|
<NTreeNodeSwitcher
|
||||||
{this.icon ? this.icon() : null}
|
clsPrefix={clsPrefix}
|
||||||
</li>
|
expanded={this.expanded}
|
||||||
|
loading={this.loading}
|
||||||
|
hide={tmNode.isLeaf}
|
||||||
|
onClick={this.handleSwitcherClick}
|
||||||
|
/>
|
||||||
|
{checkable ? (
|
||||||
|
<NTreeNodeCheckbox
|
||||||
|
clsPrefix={clsPrefix}
|
||||||
|
checked={this.checked}
|
||||||
|
indeterminate={this.indeterminate}
|
||||||
|
onCheck={this.handleCheck}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<NTreeNodeContent
|
||||||
|
clsPrefix={clsPrefix}
|
||||||
|
onClick={this.handleContentClick}
|
||||||
|
onDragstart={this.handleDragStart}
|
||||||
|
{...(!blockLine ? dragEventHandlers : undefined)}
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
default: () => tmNode.rawNode.label
|
||||||
|
}}
|
||||||
|
</NTreeNodeContent>
|
||||||
|
{this.icon ? this.icon() : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -12,159 +12,31 @@ export default defineComponent({
|
|||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
onClick: Function as PropType<(e: MouseEvent) => void>,
|
onClick: Function as PropType<(e: MouseEvent) => void>,
|
||||||
onDragstart: Function as PropType<(e: DragEvent) => void>,
|
onDragstart: Function as PropType<(e: DragEvent) => void>
|
||||||
onDragend: Function as PropType<(e: DragEvent) => void>,
|
|
||||||
onDragenter: Function as PropType<(e: DragEvent) => void>,
|
|
||||||
onDragover: Function as PropType<(e: DragEvent) => void>,
|
|
||||||
onDragleave: Function as PropType<(e: DragEvent) => void>,
|
|
||||||
onDrop: Function as PropType<
|
|
||||||
(e: DragEvent, dropPosition: 'bottom' | 'center' | 'top') => void
|
|
||||||
>
|
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
const pendingRef = ref(false)
|
|
||||||
const pendingPositionRef = ref<'top' | 'center' | 'bottom' | null>(null)
|
|
||||||
const selfRef = ref<HTMLElement | null>(null)
|
const selfRef = ref<HTMLElement | null>(null)
|
||||||
function doClick (e: MouseEvent): void {
|
function doClick (e: MouseEvent): void {
|
||||||
const { onClick } = props
|
const { onClick } = props
|
||||||
if (onClick) onClick(e)
|
if (onClick) onClick(e)
|
||||||
}
|
}
|
||||||
function doDragStart (e: DragEvent): void {
|
|
||||||
const { onDragstart } = props
|
|
||||||
if (onDragstart) onDragstart(e)
|
|
||||||
}
|
|
||||||
function doDragEnter (e: DragEvent): void {
|
|
||||||
const { onDragenter } = props
|
|
||||||
if (onDragenter) onDragenter(e)
|
|
||||||
}
|
|
||||||
function doDragEnd (e: DragEvent): void {
|
|
||||||
const { onDragend } = props
|
|
||||||
if (onDragend) onDragend(e)
|
|
||||||
}
|
|
||||||
function doDragLeave (e: DragEvent): void {
|
|
||||||
const { onDragleave } = props
|
|
||||||
if (onDragleave) onDragleave(e)
|
|
||||||
}
|
|
||||||
// function doDragOver (e: DragEvent) {
|
|
||||||
// const { onDragOver } = props
|
|
||||||
// if (onDragOver) onDragOver(e)
|
|
||||||
// }
|
|
||||||
function doDrop (
|
|
||||||
e: DragEvent,
|
|
||||||
dropPosition: 'top' | 'bottom' | 'center'
|
|
||||||
): void {
|
|
||||||
const { onDrop } = props
|
|
||||||
if (onDrop) onDrop(e, dropPosition)
|
|
||||||
}
|
|
||||||
function handleClick (e: MouseEvent): void {
|
function handleClick (e: MouseEvent): void {
|
||||||
doClick(e)
|
doClick(e)
|
||||||
}
|
}
|
||||||
function handleContentDragStart (e: DragEvent): void {
|
|
||||||
doDragStart(e)
|
|
||||||
}
|
|
||||||
function handleContentDragEnter (e: DragEvent): void {
|
|
||||||
if (
|
|
||||||
e.currentTarget &&
|
|
||||||
e.relatedTarget &&
|
|
||||||
(e.currentTarget as HTMLElement).contains(
|
|
||||||
e.relatedTarget as HTMLElement
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
doDragEnter(e)
|
|
||||||
}
|
|
||||||
function handleDragOverContent (e: DragEvent): void {
|
|
||||||
e.preventDefault()
|
|
||||||
const el = selfRef.value as HTMLElement
|
|
||||||
pendingRef.value = true
|
|
||||||
const elOffsetHeight = el.offsetHeight
|
|
||||||
const elClientTop = el.getBoundingClientRect().top
|
|
||||||
const eventOffsetY = e.clientY - elClientTop
|
|
||||||
if (eventOffsetY <= 8) {
|
|
||||||
pendingPositionRef.value = 'top'
|
|
||||||
} else if (eventOffsetY >= elOffsetHeight - 8) {
|
|
||||||
pendingPositionRef.value = 'bottom'
|
|
||||||
} else {
|
|
||||||
pendingPositionRef.value = 'center'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function handleContentDragEnd (e: DragEvent): void {
|
|
||||||
doDragEnd(e)
|
|
||||||
}
|
|
||||||
function handleContentDragLeave (e: DragEvent): void {
|
|
||||||
if (
|
|
||||||
e.currentTarget &&
|
|
||||||
e.relatedTarget &&
|
|
||||||
(e.currentTarget as HTMLElement).contains(
|
|
||||||
e.relatedTarget as HTMLElement
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pendingRef.value = false
|
|
||||||
doDragLeave(e)
|
|
||||||
}
|
|
||||||
function handleContentDrop (e: DragEvent): void {
|
|
||||||
e.preventDefault()
|
|
||||||
pendingRef.value = false
|
|
||||||
if (pendingPositionRef.value !== null) {
|
|
||||||
const dropPosition = {
|
|
||||||
top: 'top',
|
|
||||||
bottom: 'bottom',
|
|
||||||
center: 'center'
|
|
||||||
}[pendingPositionRef.value]
|
|
||||||
doDrop(e, dropPosition as 'top' | 'bottom' | 'center')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
selfRef,
|
selfRef,
|
||||||
pending: pendingRef,
|
|
||||||
pendingPosition: pendingPositionRef,
|
|
||||||
handleContentDragLeave,
|
|
||||||
handleContentDragStart,
|
|
||||||
handleDragOverContent,
|
|
||||||
handleContentDragEnd,
|
|
||||||
handleContentDragEnter,
|
|
||||||
handleContentDrop,
|
|
||||||
handleClick
|
handleClick
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render () {
|
render () {
|
||||||
const {
|
const { clsPrefix, handleClick, onDragstart } = this
|
||||||
clsPrefix,
|
|
||||||
pending,
|
|
||||||
pendingPosition,
|
|
||||||
handleContentDragLeave,
|
|
||||||
handleContentDragStart,
|
|
||||||
handleDragOverContent,
|
|
||||||
handleContentDragEnd,
|
|
||||||
handleContentDragEnter,
|
|
||||||
handleContentDrop,
|
|
||||||
handleClick
|
|
||||||
} = this
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
ref="selfRef"
|
ref="selfRef"
|
||||||
class={[
|
class={[`${clsPrefix}-tree-node-content`]}
|
||||||
`${clsPrefix}-tree-node-content`,
|
|
||||||
{
|
|
||||||
[`${clsPrefix}-tree-node-content--pending`]: pending,
|
|
||||||
[`${clsPrefix}-tree-node-content--pending-bottom`]:
|
|
||||||
pendingPosition === 'bottom',
|
|
||||||
[`${clsPrefix}-tree-node-content--pending-body`]:
|
|
||||||
pendingPosition === 'center',
|
|
||||||
[`${clsPrefix}-tree-node-content--pending-top`]:
|
|
||||||
pendingPosition === 'top'
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
onDragleave={handleContentDragLeave}
|
|
||||||
onDragstart={handleContentDragStart}
|
|
||||||
onDragover={handleDragOverContent}
|
|
||||||
onDragend={handleContentDragEnd}
|
|
||||||
onDragenter={handleContentDragEnter}
|
|
||||||
onDrop={handleContentDrop}
|
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
draggable={!!onDragstart}
|
||||||
|
onDragstart={onDragstart}
|
||||||
>
|
>
|
||||||
<div class={`${clsPrefix}-tree-node-content__padding-box`} />
|
<div class={`${clsPrefix}-tree-node-content__padding-box`} />
|
||||||
<div class={`${clsPrefix}-tree-node-content__text`}>{this.$slots}</div>
|
<div class={`${clsPrefix}-tree-node-content__text`}>{this.$slots}</div>
|
||||||
|
@ -50,6 +50,10 @@ export interface TreeInjection {
|
|||||||
checkableRef: Ref<boolean>
|
checkableRef: Ref<boolean>
|
||||||
mergedThemeRef: Ref<MergedTheme<TreeTheme>>
|
mergedThemeRef: Ref<MergedTheme<TreeTheme>>
|
||||||
onLoadRef: Ref<((node: TreeOption) => Promise<void>) | undefined>
|
onLoadRef: Ref<((node: TreeOption) => Promise<void>) | undefined>
|
||||||
|
blockLineRef: Ref<boolean>
|
||||||
|
draggingNodeRef: Ref<TmNode | null>
|
||||||
|
droppingNodeRef: Ref<TmNode | null>
|
||||||
|
droppingNodeParentRef: Ref<TmNode | null>
|
||||||
handleSwitcherClick: (node: TreeNode<TreeOption>) => void
|
handleSwitcherClick: (node: TreeNode<TreeOption>) => void
|
||||||
handleSelect: (node: TreeNode<TreeOption>) => void
|
handleSelect: (node: TreeNode<TreeOption>) => void
|
||||||
handleCheck: (node: TreeNode<TreeOption>, checked: boolean) => void
|
handleCheck: (node: TreeNode<TreeOption>, checked: boolean) => void
|
||||||
@ -57,6 +61,7 @@ export interface TreeInjection {
|
|||||||
handleDragEnter: (info: InternalDragInfo) => void
|
handleDragEnter: (info: InternalDragInfo) => void
|
||||||
handleDragLeave: (info: InternalDragInfo) => void
|
handleDragLeave: (info: InternalDragInfo) => void
|
||||||
handleDragEnd: (info: InternalDragInfo) => void
|
handleDragEnd: (info: InternalDragInfo) => void
|
||||||
|
handleDragOver: (info: InternalDragInfo) => void
|
||||||
handleDrop: (info: InternalDropInfo) => void
|
handleDrop: (info: InternalDropInfo) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
src/tree/src/render-drop-mark.tsx
Normal file
21
src/tree/src/render-drop-mark.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { CSSProperties, h, VNode } from 'vue'
|
||||||
|
|
||||||
|
export function renderDropMark (position: 'top' | 'center' | 'bottom'): VNode {
|
||||||
|
const style: CSSProperties = {
|
||||||
|
position: 'absolute',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
right: 0,
|
||||||
|
left: 0
|
||||||
|
}
|
||||||
|
if (position === 'center') {
|
||||||
|
style.top = 0
|
||||||
|
style.bottom = 0
|
||||||
|
style.borderRadius = 'inherit'
|
||||||
|
style.border = '2px solid black'
|
||||||
|
} else {
|
||||||
|
style[position] = 0
|
||||||
|
style.height = '2px'
|
||||||
|
style.backgroundColor = 'black'
|
||||||
|
}
|
||||||
|
return <div style={style}></div>
|
||||||
|
}
|
@ -8,21 +8,7 @@ const nodeStateStyle = [
|
|||||||
}),
|
}),
|
||||||
c('&:active', {
|
c('&:active', {
|
||||||
backgroundColor: 'var(--node-color-pressed)'
|
backgroundColor: 'var(--node-color-pressed)'
|
||||||
}),
|
})
|
||||||
cM('pending', [
|
|
||||||
c('&:hover', {
|
|
||||||
backgroundColor: '#0000'
|
|
||||||
}),
|
|
||||||
cM('pending-bottom', {
|
|
||||||
borderBottom: '3px solid var(--node-color-hover)'
|
|
||||||
}),
|
|
||||||
cM('pending-top', {
|
|
||||||
borderTop: '3px solid var(--node-color-hover)'
|
|
||||||
}),
|
|
||||||
cM('pending-body', {
|
|
||||||
backgroundColor: 'var(--node-color-hover)'
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]
|
]
|
||||||
|
|
||||||
// vars:
|
// vars:
|
||||||
@ -67,8 +53,8 @@ export default cB('tree', {
|
|||||||
})
|
})
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
|
cB('tree-node-wrapper', 'padding: 3px 0;'),
|
||||||
cB('tree-node', `
|
cB('tree-node', `
|
||||||
margin: 6px 0 0 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
border-radius: var(--node-border-radius);
|
border-radius: var(--node-border-radius);
|
||||||
transition: background-color .3s var(--bezier);
|
transition: background-color .3s var(--bezier);
|
||||||
@ -196,20 +182,6 @@ export default cB('tree', {
|
|||||||
}),
|
}),
|
||||||
c('&:active', {
|
c('&:active', {
|
||||||
backgroundColor: 'var(--node-color-pressed)'
|
backgroundColor: 'var(--node-color-pressed)'
|
||||||
}),
|
})
|
||||||
cM('pending', [
|
|
||||||
c('&:hover', {
|
|
||||||
backgroundColor: '#0000'
|
|
||||||
}),
|
|
||||||
cM('pending-bottom', {
|
|
||||||
borderBottom: '3px solid var(--node-color-hover)'
|
|
||||||
}),
|
|
||||||
cM('pending-top', {
|
|
||||||
borderTop: '3px solid var(--node-color-hover)'
|
|
||||||
}),
|
|
||||||
cM('pending-body', {
|
|
||||||
backgroundColor: 'var(--node-color-hover)'
|
|
||||||
})
|
|
||||||
])
|
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
Loading…
Reference in New Issue
Block a user