LetterSpacing.js (3521B)
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 createFactory, 9 PureComponent, 10 } = require("resource://devtools/client/shared/vendor/react.mjs"); 11 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 12 13 const FontPropertyValue = createFactory( 14 require("resource://devtools/client/inspector/fonts/components/FontPropertyValue.js") 15 ); 16 17 const { 18 getStr, 19 } = require("resource://devtools/client/inspector/fonts/utils/l10n.js"); 20 const { 21 getUnitFromValue, 22 getStepForUnit, 23 } = require("resource://devtools/client/inspector/fonts/utils/font-utils.js"); 24 25 class LetterSpacing extends PureComponent { 26 static get propTypes() { 27 return { 28 disabled: PropTypes.bool.isRequired, 29 onChange: PropTypes.func.isRequired, 30 value: PropTypes.string.isRequired, 31 }; 32 } 33 34 constructor(props) { 35 super(props); 36 // Local state for min/max bounds indexed by unit to allow user input that 37 // goes out-of-bounds while still providing a meaningful default range. The indexing 38 // by unit is needed to account for unit conversion (ex: em to px) where the operation 39 // may result in out-of-bounds values. Avoiding React's state and setState() because 40 // `value` is a prop coming from the Redux store while min/max are local. Reconciling 41 // value/unit changes is needlessly complicated and adds unnecessary re-renders. 42 this.historicMin = {}; 43 this.historicMax = {}; 44 } 45 46 getDefaultMinMax(unit) { 47 let min; 48 let max; 49 switch (unit) { 50 case "px": 51 min = -10; 52 max = 10; 53 break; 54 default: 55 min = -0.2; 56 max = 0.6; 57 break; 58 } 59 60 return { min, max }; 61 } 62 63 render() { 64 // For a unitless or a NaN value, default unit to "em". 65 const unit = getUnitFromValue(this.props.value) || "em"; 66 // When the initial value of "letter-spacing" is "normal", the parsed value 67 // is not a number (NaN). Guard by setting the default value to 0. 68 const isKeywordValue = this.props.value === "normal"; 69 const value = isKeywordValue ? 0 : parseFloat(this.props.value); 70 71 let { min, max } = this.getDefaultMinMax(unit); 72 min = Math.min(min, value); 73 max = Math.max(max, value); 74 // Allow lower and upper bounds to move to accomodate the incoming value. 75 this.historicMin[unit] = this.historicMin[unit] 76 ? Math.min(this.historicMin[unit], min) 77 : min; 78 this.historicMax[unit] = this.historicMax[unit] 79 ? Math.max(this.historicMax[unit], max) 80 : max; 81 82 return FontPropertyValue({ 83 allowOverflow: true, 84 allowUnderflow: true, 85 disabled: this.props.disabled, 86 label: getStr("fontinspector.letterSpacingLabel"), 87 min: this.historicMin[unit], 88 max: this.historicMax[unit], 89 name: "letter-spacing", 90 onChange: this.props.onChange, 91 // Increase the increment granularity because letter spacing is very sensitive. 92 step: getStepForUnit(unit) / 100, 93 // Show the value input and unit only when the value is not a keyword. 94 showInput: !isKeywordValue, 95 showUnit: !isKeywordValue, 96 unit, 97 unitOptions: ["em", "rem", "px"], 98 value, 99 // Show the value as a read-only label if it's a keyword. 100 valueLabel: isKeywordValue ? this.props.value : null, 101 }); 102 } 103 } 104 105 module.exports = LetterSpacing;