/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ import {DataInspectorExpanded} from './DataInspector'; import {PureComponent} from 'react'; import DataInspector from './DataInspector'; import React from 'react'; import {DataValueExtractor} from './DataPreview'; import {HighlightProvider} from '../Highlight'; export type ManagedDataInspectorProps = { /** * Object to inspect. */ data: any; /** * Object to compare with the provided `data` property. * Differences will be styled accordingly in the UI. */ diff?: any; /** * Whether to expand the root by default. */ expandRoot?: boolean; /** * An optional callback that will explode a value into its type and value. * Useful for inspecting serialised data. */ extractValue?: DataValueExtractor; /** * Callback when a value is edited. */ setValue?: (path: Array, val: any) => void; /** * Callback when a delete action is invoked. */ onDelete?: (path: Array) => void; /** * Whether all objects and arrays should be collapsed by default. */ collapsed?: boolean; /** * Object of all properties that will have tooltips */ tooltips?: Object; /** * Filter nodes by some search text */ filter?: string; }; type ManagedDataInspectorState = { expanded: DataInspectorExpanded; filterExpanded: DataInspectorExpanded; userExpanded: DataInspectorExpanded; filter: string; }; const MAX_RESULTS = 50; const EMPTY_ARRAY: any[] = []; /** * Wrapper around `DataInspector` that handles expanded state. * * If you require lower level access to the state then use `DataInspector` * directly. */ export default class ManagedDataInspector extends PureComponent< ManagedDataInspectorProps, ManagedDataInspectorState > { constructor(props: ManagedDataInspectorProps, context: Object) { super(props, context); this.state = { expanded: {}, userExpanded: {}, filterExpanded: {}, filter: '', }; } static getDerivedStateFromProps( nextProps: ManagedDataInspectorProps, currentState: ManagedDataInspectorState, ) { if (nextProps.filter?.toLowerCase() === currentState.filter) { return null; } if (!nextProps.filter) { return { filter: '', filterExpanded: {}, // reset expanded when removing filter expanded: currentState.userExpanded, }; } const filter = nextProps.filter!.toLowerCase(); const paths: (number | string)[][] = []; function walk(value: any, path: (number | string)[]) { if (paths.length > MAX_RESULTS) { return; } if (!value) { return; } if (typeof value !== 'object') { if (('' + value).toLowerCase().includes(filter!)) { paths.push(path.slice()); } } else if (Array.isArray(value)) { value.forEach((value, index) => { path.push(index); walk(value, path); path.pop(); }); } else { // a plain object Object.keys(value).forEach((key) => { path.push(key); walk(key, path); // is the key interesting? walk(value[key], path); path.pop(); }); } } if (filter.length >= 2) { walk(nextProps.data, []); } const filterExpanded: Record = {}; paths.forEach((path) => { for (let i = 1; i < path.length; i++) filterExpanded[path.slice(0, i).join('.')] = true; }); return { filterExpanded, expanded: {...currentState.userExpanded, ...filterExpanded}, filter, }; } onExpanded = (path: string, isExpanded: boolean) => { this.setState({ userExpanded: { ...this.state.userExpanded, [path]: isExpanded, }, expanded: { ...this.state.expanded, [path]: isExpanded, }, }); }; render() { return ( ); } }