Blame view

天文台pc/tianwentai-ui/node_modules/react-dnd/src/internals/wrapConnectorHooks.ts 2.78 KB
bc518174   王天杨   提交两个项目文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  import { invariant } from '@react-dnd/invariant'
  import type { ReactElement } from 'react'
  import { cloneElement, isValidElement } from 'react'
  
  function throwIfCompositeComponentElement(element: ReactElement<any>) {
  	// Custom components can no longer be wrapped directly in React DnD 2.0
  	// so that we don't need to depend on findDOMNode() from react-dom.
  	if (typeof element.type === 'string') {
  		return
  	}
  
  	const displayName =
  		(element.type as any).displayName || element.type.name || 'the component'
  
  	throw new Error(
  		'Only native element nodes can now be passed to React DnD connectors.' +
  			`You can either wrap ${displayName} into a <div>, or turn it into a ` +
  			'drag source or a drop target itself.',
  	)
  }
  
  function wrapHookToRecognizeElement(hook: (node: any, options: any) => void) {
  	return (elementOrNode = null, options = null) => {
  		// When passed a node, call the hook straight away.
  		if (!isValidElement(elementOrNode)) {
  			const node = elementOrNode
  			hook(node, options)
  			// return the node so it can be chained (e.g. when within callback refs
  			// <div ref={node => connectDragSource(connectDropTarget(node))}/>
  			return node
  		}
  
  		// If passed a ReactElement, clone it and attach this function as a ref.
  		// This helps us achieve a neat API where user doesn't even know that refs
  		// are being used under the hood.
  		const element: ReactElement | null = elementOrNode
  		throwIfCompositeComponentElement(element as any)
  
  		// When no options are passed, use the hook directly
  		const ref = options ? (node: Element) => hook(node, options) : hook
  		return cloneWithRef(element, ref)
  	}
  }
  
  export function wrapConnectorHooks(hooks: any) {
  	const wrappedHooks: any = {}
  
  	Object.keys(hooks).forEach((key) => {
  		const hook = hooks[key]
  
  		// ref objects should be passed straight through without wrapping
  		if (key.endsWith('Ref')) {
  			wrappedHooks[key] = hooks[key]
  		} else {
  			const wrappedHook = wrapHookToRecognizeElement(hook)
  			wrappedHooks[key] = () => wrappedHook
  		}
  	})
  
  	return wrappedHooks
  }
  
  function setRef(ref: any, node: any) {
  	if (typeof ref === 'function') {
  		ref(node)
  	} else {
  		ref.current = node
  	}
  }
  
  function cloneWithRef(element: any, newRef: any): ReactElement<any> {
  	const previousRef = element.ref
  	invariant(
  		typeof previousRef !== 'string',
  		'Cannot connect React DnD to an element with an existing string ref. ' +
  			'Please convert it to use a callback ref instead, or wrap it into a <span> or <div>. ' +
  			'Read more: https://reactjs.org/docs/refs-and-the-dom.html#callback-refs',
  	)
  
  	if (!previousRef) {
  		// When there is no ref on the element, use the new ref directly
  		return cloneElement(element, {
  			ref: newRef,
  		})
  	} else {
  		return cloneElement(element, {
  			ref: (node: any) => {
  				setRef(previousRef, node)
  				setRef(newRef, node)
  			},
  		})
  	}
  }