InputMap.js (6272B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 Component, 9 createRef, 10 } = require("resource://devtools/client/shared/vendor/react.mjs"); 11 const { 12 L10N, 13 } = require("resource://devtools/client/netmonitor/src/utils/l10n.js"); 14 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 15 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 16 const { div, input, textarea, button } = dom; 17 18 const CUSTOM_NEW_REQUEST_INPUT_NAME = L10N.getStr( 19 "netmonitor.custom.placeholder.name" 20 ); 21 22 const CUSTOM_NEW_REQUEST_INPUT_VALUE = L10N.getStr( 23 "netmonitor.custom.placeholder.value" 24 ); 25 26 const REMOVE_ITEM = L10N.getStr("netmonitor.custom.removeItem"); 27 28 /** 29 * Editable name and value list component with optional form to add new items 30 */ 31 class InputMap extends Component { 32 static get propTypes() { 33 return { 34 list: PropTypes.arrayOf( 35 PropTypes.shape({ 36 name: PropTypes.string.isRequired, 37 value: PropTypes.string.isRequired, 38 disabled: PropTypes.bool, 39 }) 40 ).isRequired, 41 onUpdate: PropTypes.func, 42 onAdd: PropTypes.func, 43 onDelete: PropTypes.func, 44 onChange: PropTypes.func, 45 onChecked: PropTypes.func, 46 }; 47 } 48 49 constructor(props) { 50 super(props); 51 52 this.listRef = createRef(); 53 54 this.state = { 55 name: "", 56 value: "", 57 }; 58 } 59 60 render() { 61 const { list, onUpdate, onAdd, onDelete, onChecked } = this.props; 62 const { name, value } = this.state; 63 64 // Adds a new item with name and value when the user starts typing on the form 65 const onKeyDown = event => { 66 const { target } = event; 67 onAdd(name, value); 68 this.setState({ name: "", value: "" }, () => { 69 // Get next to last child on the list, 70 // because that was the item that was just added and 71 // we need to focous on it, so the user can keep editing it. 72 const targetParentNode = 73 this.listRef.current.childNodes?.[ 74 this.listRef.current.childElementCount - 2 75 ]; 76 targetParentNode?.querySelector(`.${target.className}`).focus(); 77 }); 78 }; 79 80 return div( 81 { 82 ref: this.listRef, 83 className: "http-custom-input-and-map-form", 84 }, 85 list.map((item, index) => { 86 return div( 87 { 88 className: "tabpanel-summary-container http-custom-input", 89 id: `http-custom-${item.name.toLowerCase()}`, 90 key: index, 91 }, 92 input({ 93 className: "tabpanel-summary-input-checkbox", 94 name: `checked-${index}`, 95 type: "checkbox", 96 onChange: event => { 97 onChecked(index, event.target.checked); 98 }, 99 checked: item.checked, 100 disabled: !!item.disabled, 101 wrap: "off", 102 }), 103 div( 104 { className: "tabpanel-summary-input-name" }, 105 div( 106 { 107 className: "auto-growing-textarea", 108 "data-replicated-value": item.name, 109 title: item.name, 110 }, 111 textarea({ 112 className: "http-custom-input-name", 113 name: `name-${index}`, 114 value: item.name, 115 disabled: !!item.disabled, 116 onChange: event => { 117 onUpdate(event); 118 }, 119 rows: 1, 120 }) 121 ) 122 ), 123 div( 124 { className: "tabpanel-summary-input-value" }, 125 div( 126 { 127 className: "auto-growing-textarea", 128 "data-replicated-value": item.value, 129 title: item.value, 130 }, 131 textarea({ 132 className: "http-custom-input-value", 133 name: `value-${index}`, 134 placeholder: "value", 135 disabled: !!item.disabled, 136 onChange: event => { 137 onUpdate(event); 138 }, 139 value: item.value, 140 rows: 1, 141 }) 142 ) 143 ), 144 !item.disabled && 145 onDelete && 146 button({ 147 className: "http-custom-delete-button", 148 title: REMOVE_ITEM, 149 "aria-label": REMOVE_ITEM, 150 onClick: () => onDelete(index), 151 }) 152 ); 153 }), 154 onAdd && 155 div( 156 { 157 className: "map-add-new-inputs", 158 }, 159 input({ 160 className: "tabpanel-summary-input-checkbox", 161 onChange: () => {}, 162 checked: true, 163 type: "checkbox", 164 }), 165 div( 166 { className: "tabpanel-summary-input-name" }, 167 div( 168 { 169 className: "auto-growing-textarea", 170 "data-replicated-value": name, 171 title: value, 172 }, 173 textarea({ 174 className: "http-custom-input-name", 175 type: "text", 176 ref: "addInputName", 177 checked: true, 178 value: name, 179 rows: 1, 180 placeholder: CUSTOM_NEW_REQUEST_INPUT_NAME, 181 onChange: e => this.setState({ name: e.target.value }), 182 onKeyDown, 183 }) 184 ) 185 ), 186 div( 187 { className: "tabpanel-summary-input-value" }, 188 div( 189 { 190 className: "auto-growing-textarea", 191 "data-replicated-value": value, 192 title: value, 193 }, 194 textarea({ 195 className: "http-custom-input-value", 196 type: "text", 197 ref: "addInputValue", 198 value, 199 onChange: e => this.setState({ value: e.target.value }), 200 rows: 1, 201 placeholder: CUSTOM_NEW_REQUEST_INPUT_VALUE, 202 onKeyDown, 203 }) 204 ) 205 ) 206 ) 207 ); 208 } 209 } 210 211 module.exports = InputMap;