main
js 108 lines 3.21 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import {copy} from 'clipboard-js';
11 import * as React from 'react';
12 import Button from '../Button';
13 import ButtonIcon from '../ButtonIcon';
14 import KeyValue from './KeyValue';
15 import {alphaSortEntries, serializeDataForCopy} from '../utils';
16 import Store from '../../store';
17 import styles from './InspectedElementSharedStyles.css';
18 import {
19 ElementTypeClass,
20 ElementTypeFunction,
21 } from 'react-devtools-shared/src/frontend/types';
22 import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
23
24 import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
25 import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
26 import type {Element} from 'react-devtools-shared/src/frontend/types';
27
28 type Props = {
29 bridge: FrontendBridge,
30 element: Element,
31 inspectedElement: InspectedElement,
32 store: Store,
33 };
34
35 export default function InspectedElementContextTree({
36 bridge,
37 element,
38 inspectedElement,
39 store,
40 }: Props): React.Node {
41 const {hasLegacyContext, context, type} = inspectedElement;
42
43 const isReadOnly = type !== ElementTypeClass && type !== ElementTypeFunction;
44
45 if (context == null) {
46 return null;
47 }
48
49 const entries = Object.entries(context);
50 entries.sort(alphaSortEntries);
51 const isEmpty = entries.length === 0;
52
53 const handleCopy = withPermissionsCheck(
54 {permissions: ['clipboardWrite']},
55 () => copy(serializeDataForCopy(context)),
56 );
57
58 // We add an object with a "value" key as a wrapper around Context data
59 // so that we can use the shared <KeyValue> component to display it.
60 // This wrapper object can't be renamed.
61 // $FlowFixMe[missing-local-annot]
62 const canRenamePathsAtDepth = depth => depth > 1;
63
64 if (isEmpty) {
65 return null;
66 } else {
67 return (
68 <div>
69 <div className={styles.HeaderRow}>
70 <div className={styles.Header}>
71 {hasLegacyContext ? 'legacy context' : 'context'}
72 </div>
73 {/* $FlowFixMe[constant-condition] */}
74 {!isEmpty && (
75 <Button onClick={handleCopy} title="Copy to clipboard">
76 <ButtonIcon type="copy" />
77 </Button>
78 )}
79 </div>
80 {/* $FlowFixMe[constant-condition] */}
81 {isEmpty && <div className={styles.Empty}>None</div>}
82 {/* $FlowFixMe[constant-condition] */}
83 {!isEmpty &&
84 (entries as any).map(([name, value]) => (
85 <KeyValue
86 key={name}
87 alphaSort={true}
88 bridge={bridge}
89 canDeletePaths={!isReadOnly}
90 canEditValues={!isReadOnly}
91 canRenamePaths={!isReadOnly}
92 canRenamePathsAtDepth={canRenamePathsAtDepth}
93 depth={1}
94 element={element}
95 hidden={false}
96 inspectedElement={inspectedElement}
97 name={name}
98 path={[name]}
99 pathRoot="context"
100 store={store}
101 type="context"
102 value={value}
103 />
104 ))}
105 </div>
106 );
107 }
108 }