1 | /*
|
2 | * The MIT License (MIT)
|
3 | *
|
4 | * Copyright (c) 2015 - present Instructure, Inc.
|
5 | *
|
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | * of this software and associated documentation files (the "Software"), to deal
|
8 | * in the Software without restriction, including without limitation the rights
|
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | * copies of the Software, and to permit persons to whom the Software is
|
11 | * furnished to do so, subject to the following conditions:
|
12 | *
|
13 | * The above copyright notice and this permission notice shall be included in all
|
14 | * copies or substantial portions of the Software.
|
15 | *
|
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22 | * SOFTWARE.
|
23 | */
|
24 | import React from 'react';
|
25 | import { logWarn as warn } from '@instructure/console';
|
26 | import { createChainedFunction } from '@instructure/ui-utils';
|
27 |
|
28 | /**
|
29 | * ---
|
30 | * category: utilities/react
|
31 | * ---
|
32 | * Clones a React element without overwriting refs.
|
33 | * @param element The element to clone
|
34 | * @param props Props of the element
|
35 | * @param children
|
36 | */
|
37 | function safeCloneElement(element, props) {
|
38 | const cloneRef = props.ref;
|
39 | const originalRef = element.ref;
|
40 | const originalRefIsAFunction = typeof originalRef === 'function';
|
41 | const cloneRefIsFunction = typeof cloneRef === 'function';
|
42 | const mergedProps = { ...props
|
43 | };
|
44 |
|
45 | if (element.props.style && props.style) {
|
46 | // merge with existing styles
|
47 | mergedProps.style = { ...element.props.style,
|
48 | ...props.style
|
49 | };
|
50 | } // prevent overriding existing keys
|
51 |
|
52 |
|
53 | mergedProps.key = element.key || props.key; // Add chained function to preserve existing event handlers
|
54 |
|
55 | Object.keys(props).forEach(prop => {
|
56 | // If prop looks like an event handler "on*" and either
|
57 | // props[props] or element.props[prop] is a function create a chained function.
|
58 | // If only one is a function it will just use that function with no extra overhead.
|
59 | // This is necessary in cases where props[prop] is `null` or `undefined` which would
|
60 | // otherwise unwantedly override element.props[prop].
|
61 | if (prop.indexOf('on') === 0 && (typeof props[prop] === 'function' || typeof element.props[prop] === 'function')) {
|
62 | ;
|
63 | mergedProps[prop] = createChainedFunction(element.props[prop], props[prop]);
|
64 | }
|
65 | });
|
66 |
|
67 | for (var _len = arguments.length, children = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
|
68 | children[_key - 2] = arguments[_key];
|
69 | }
|
70 |
|
71 | if (originalRef == null || cloneRef == null) {
|
72 | return /*#__PURE__*/React.cloneElement(element, mergedProps, ...children);
|
73 | }
|
74 |
|
75 | warn(originalRefIsAFunction, `Cloning an element with a ref that will be overwritten because the ref \
|
76 | is not a function. Use a composable callback-style ref instead. \
|
77 | Ignoring ref: ${originalRef}`);
|
78 | return /*#__PURE__*/React.cloneElement(element, { ...mergedProps,
|
79 |
|
80 | ref(component) {
|
81 | if (cloneRefIsFunction) {
|
82 | ;
|
83 | cloneRef(component);
|
84 | } else {
|
85 | cloneRef.current = component;
|
86 | }
|
87 |
|
88 | originalRef(component);
|
89 | }
|
90 |
|
91 | }, ...children);
|
92 | }
|
93 |
|
94 | export default safeCloneElement;
|
95 | export { safeCloneElement }; |
\ | No newline at end of file |