diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /devtools/client/netmonitor/src/components/request-details/PropertiesView.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/netmonitor/src/components/request-details/PropertiesView.js')
-rw-r--r-- | devtools/client/netmonitor/src/components/request-details/PropertiesView.js | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/components/request-details/PropertiesView.js b/devtools/client/netmonitor/src/components/request-details/PropertiesView.js new file mode 100644 index 0000000000..759653c1aa --- /dev/null +++ b/devtools/client/netmonitor/src/components/request-details/PropertiesView.js @@ -0,0 +1,247 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* eslint-disable react/prop-types */ + +"use strict"; + +const { + Component, + createFactory, +} = require("resource://devtools/client/shared/vendor/react.js"); +const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); +const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js"); +const { + connect, +} = require("resource://devtools/client/shared/redux/visibility-handler-connect.js"); +const { + setTargetSearchResult, +} = require("resource://devtools/client/netmonitor/src/actions/search.js"); + +// Components +const TreeViewClass = require("resource://devtools/client/shared/components/tree/TreeView.js"); +const TreeView = createFactory(TreeViewClass); +const PropertiesViewContextMenu = require("resource://devtools/client/netmonitor/src/widgets/PropertiesViewContextMenu.js"); + +loader.lazyGetter(this, "Rep", function () { + return require("resource://devtools/client/shared/components/reps/index.js") + .REPS.Rep; +}); +loader.lazyGetter(this, "MODE", function () { + return require("resource://devtools/client/shared/components/reps/index.js") + .MODE; +}); + +// Constants +const { + AUTO_EXPAND_MAX_LEVEL, + AUTO_EXPAND_MAX_NODES, +} = require("resource://devtools/client/netmonitor/src/constants.js"); + +const { div } = dom; + +/** + * Properties View component + * A scrollable tree view component which provides some useful features for + * representing object properties. + * + * Tree view + * Rep + */ +class PropertiesView extends Component { + static get propTypes() { + return { + object: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), + provider: PropTypes.object, + enableInput: PropTypes.bool, + expandableStrings: PropTypes.bool, + expandedNodes: PropTypes.object, + useBaseTreeViewExpand: PropTypes.bool, + filterText: PropTypes.string, + cropLimit: PropTypes.number, + targetSearchResult: PropTypes.object, + resetTargetSearchResult: PropTypes.func, + selectPath: PropTypes.func, + mode: PropTypes.symbol, + defaultSelectFirstNode: PropTypes.bool, + useQuotes: PropTypes.bool, + onClickRow: PropTypes.func, + contextMenuFormatters: PropTypes.object, + }; + } + + static get defaultProps() { + return { + enableInput: true, + enableFilter: true, + expandableStrings: false, + cropLimit: 1024, + useQuotes: true, + contextMenuFormatters: {}, + useBaseTreeViewExpand: false, + }; + } + + constructor(props) { + super(props); + this.onFilter = this.onFilter.bind(this); + this.renderValueWithRep = this.renderValueWithRep.bind(this); + this.getSelectedPath = this.getSelectedPath.bind(this); + } + + /** + * Update only if: + * 1) The rendered object has changed + * 2) The filter text has changed + * 3) The user selected another search result target. + */ + shouldComponentUpdate(nextProps) { + return ( + this.props.object !== nextProps.object || + this.props.filterText !== nextProps.filterText || + (this.props.targetSearchResult !== nextProps.targetSearchResult && + nextProps.targetSearchResult !== null) + ); + } + + onFilter(props) { + const { name, value } = props; + const { filterText } = this.props; + + if (!filterText) { + return true; + } + + const jsonString = JSON.stringify({ [name]: value }).toLowerCase(); + return jsonString.includes(filterText.toLowerCase()); + } + + getSelectedPath(targetSearchResult) { + if (!targetSearchResult) { + return null; + } + + return `/${targetSearchResult.label}`; + } + + /** + * If target is selected, let's scroll the content + * so the property is visible. This is used for search result navigation, + * which happens when the user clicks on a search result. + */ + scrollSelectedIntoView() { + const { targetSearchResult, resetTargetSearchResult, selectPath } = + this.props; + if (!targetSearchResult) { + return; + } + + const path = + typeof selectPath == "function" + ? selectPath(targetSearchResult) + : this.getSelectedPath(targetSearchResult); + const element = document.getElementById(path); + if (element) { + element.scrollIntoView({ block: "center" }); + } + + resetTargetSearchResult(); + } + + onContextMenuRow(member, evt) { + evt.preventDefault(); + + const { object } = member; + + // Select the right clicked row + this.selectRow({ props: { member } }); + + // if data exists and can be copied, then show the contextmenu + if (typeof object === "object") { + if (!this.contextMenu) { + this.contextMenu = new PropertiesViewContextMenu({ + customFormatters: this.props.contextMenuFormatters, + }); + } + this.contextMenu.open(evt, window.getSelection(), { + member, + object: this.props.object, + }); + } + } + + renderValueWithRep(props) { + const { member } = props; + + /* Hide strings with following conditions + * - the `value` object has a `value` property (only happens in Cookies panel) + */ + if (typeof member.value === "object" && member.value?.value) { + return null; + } + + return Rep( + Object.assign(props, { + // FIXME: A workaround for the issue in StringRep + // Force StringRep to crop the text every time + member: Object.assign({}, member, { open: false }), + mode: this.props.mode || MODE.TINY, + cropLimit: this.props.cropLimit, + noGrip: true, + }) + ); + } + + render() { + const { + useBaseTreeViewExpand, + expandedNodes, + object, + renderValue, + targetSearchResult, + selectPath, + } = this.props; + + let currentExpandedNodes; + // In the TreeView, when the component is re-rendered + // the state of `expandedNodes` is persisted by default + // e.g. when you open a node and filter the properties list, + // the node remains open. + // We have the prop `useBaseTreeViewExpand` to flag when we want to use + // this functionality or not. + if (!useBaseTreeViewExpand) { + currentExpandedNodes = + expandedNodes || + TreeViewClass.getExpandedNodes(object, { + maxLevel: AUTO_EXPAND_MAX_LEVEL, + maxNodes: AUTO_EXPAND_MAX_NODES, + }); + } + return div( + { className: "properties-view" }, + div( + { className: "tree-container" }, + TreeView({ + ...this.props, + ref: () => this.scrollSelectedIntoView(), + columns: [{ id: "value", width: "100%" }], + + expandedNodes: currentExpandedNodes, + + onFilter: props => this.onFilter(props), + renderValue: renderValue || this.renderValueWithRep, + onContextMenuRow: this.onContextMenuRow, + selected: + typeof selectPath == "function" + ? selectPath(targetSearchResult) + : this.getSelectedPath(targetSearchResult), + }) + ) + ); + } +} + +module.exports = connect(null, dispatch => ({ + resetTargetSearchResult: () => dispatch(setTargetSearchResult(null)), +}))(PropertiesView); |