MainFrame.js (7543B)
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 "use strict"; 5 6 // React & Redux 7 const { 8 Component, 9 createFactory, 10 } = require("resource://devtools/client/shared/vendor/react.mjs"); 11 const { 12 span, 13 div, 14 } = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 15 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 16 const { 17 connect, 18 } = require("resource://devtools/client/shared/vendor/react-redux.js"); 19 const { 20 enable, 21 reset, 22 updateCanBeEnabled, 23 updateCanBeDisabled, 24 } = require("resource://devtools/client/accessibility/actions/ui.js"); 25 26 // Localization 27 const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js"); 28 const LocalizationProvider = createFactory(FluentReact.LocalizationProvider); 29 30 // Constants 31 const { 32 SIDEBAR_WIDTH, 33 PORTRAIT_MODE_WIDTH, 34 } = require("resource://devtools/client/accessibility/constants.js"); 35 36 // Accessibility Panel 37 const AccessibilityTree = createFactory( 38 require("resource://devtools/client/accessibility/components/AccessibilityTree.js") 39 ); 40 const AuditProgressOverlay = createFactory( 41 require("resource://devtools/client/accessibility/components/AuditProgressOverlay.js") 42 ); 43 const Description = createFactory( 44 require("resource://devtools/client/accessibility/components/Description.js") 45 .Description 46 ); 47 const RightSidebar = createFactory( 48 require("resource://devtools/client/accessibility/components/RightSidebar.js") 49 ); 50 const Toolbar = createFactory( 51 require("resource://devtools/client/accessibility/components/Toolbar.js") 52 .Toolbar 53 ); 54 const SplitBox = createFactory( 55 require("resource://devtools/client/shared/components/splitter/SplitBox.js") 56 ); 57 58 /** 59 * Renders basic layout of the Accessibility panel. The Accessibility panel 60 * content consists of two main parts: tree and sidebar. 61 */ 62 class MainFrame extends Component { 63 static get propTypes() { 64 return { 65 fluentBundles: PropTypes.array.isRequired, 66 enabled: PropTypes.bool.isRequired, 67 dispatch: PropTypes.func.isRequired, 68 auditing: PropTypes.array.isRequired, 69 supports: PropTypes.object, 70 toolbox: PropTypes.object.isRequired, 71 getAccessibilityTreeRoot: PropTypes.func.isRequired, 72 startListeningForAccessibilityEvents: PropTypes.func.isRequired, 73 stopListeningForAccessibilityEvents: PropTypes.func.isRequired, 74 audit: PropTypes.func.isRequired, 75 simulate: PropTypes.func, 76 enableAccessibility: PropTypes.func.isRequired, 77 resetAccessiblity: PropTypes.func.isRequired, 78 startListeningForLifecycleEvents: PropTypes.func.isRequired, 79 stopListeningForLifecycleEvents: PropTypes.func.isRequired, 80 startListeningForParentLifecycleEvents: PropTypes.func.isRequired, 81 stopListeningForParentLifecycleEvents: PropTypes.func.isRequired, 82 highlightAccessible: PropTypes.func.isRequired, 83 unhighlightAccessible: PropTypes.func.isRequired, 84 }; 85 } 86 87 constructor(props) { 88 super(props); 89 90 this.resetAccessibility = this.resetAccessibility.bind(this); 91 this.onPanelWindowResize = this.onPanelWindowResize.bind(this); 92 this.onCanBeEnabledChange = this.onCanBeEnabledChange.bind(this); 93 this.onCanBeDisabledChange = this.onCanBeDisabledChange.bind(this); 94 } 95 96 componentDidMount() { 97 this.props.startListeningForLifecycleEvents({ 98 init: this.resetAccessibility, 99 shutdown: this.resetAccessibility, 100 }); 101 this.props.startListeningForParentLifecycleEvents({ 102 "can-be-enabled-change": this.onCanBeEnabledChange, 103 "can-be-disabled-change": this.onCanBeDisabledChange, 104 }); 105 this.props.startListeningForAccessibilityEvents({ 106 "top-level-document-ready": this.resetAccessibility, 107 }); 108 window.addEventListener("resize", this.onPanelWindowResize, true); 109 } 110 111 componentWillUnmount() { 112 this.props.stopListeningForLifecycleEvents({ 113 init: this.resetAccessibility, 114 shutdown: this.resetAccessibility, 115 }); 116 this.props.stopListeningForParentLifecycleEvents({ 117 "can-be-enabled-change": this.onCanBeEnabledChange, 118 "can-be-disabled-change": this.onCanBeDisabledChange, 119 }); 120 this.props.stopListeningForAccessibilityEvents({ 121 "top-level-document-ready": this.resetAccessibility, 122 }); 123 window.removeEventListener("resize", this.onPanelWindowResize, true); 124 } 125 126 resetAccessibility() { 127 const { dispatch, resetAccessiblity, supports } = this.props; 128 dispatch(reset(resetAccessiblity, supports)); 129 } 130 131 onCanBeEnabledChange(canBeEnabled) { 132 const { enableAccessibility, dispatch } = this.props; 133 dispatch(updateCanBeEnabled(canBeEnabled)); 134 if (canBeEnabled) { 135 dispatch(enable(enableAccessibility)); 136 } 137 } 138 139 onCanBeDisabledChange(canBeDisabled) { 140 this.props.dispatch(updateCanBeDisabled(canBeDisabled)); 141 } 142 143 get useLandscapeMode() { 144 const { clientWidth } = document.getElementById("content"); 145 return clientWidth > PORTRAIT_MODE_WIDTH; 146 } 147 148 /** 149 * If panel width is less than PORTRAIT_MODE_WIDTH px, the splitter changes 150 * its mode to `horizontal` to support portrait view. 151 */ 152 onPanelWindowResize() { 153 if (this.refs.splitBox) { 154 this.refs.splitBox.setState({ vert: this.useLandscapeMode }); 155 } 156 } 157 158 /** 159 * Render Accessibility panel content 160 */ 161 render() { 162 const { 163 fluentBundles, 164 enabled, 165 auditing, 166 simulate, 167 toolbox, 168 getAccessibilityTreeRoot, 169 startListeningForAccessibilityEvents, 170 stopListeningForAccessibilityEvents, 171 audit, 172 highlightAccessible, 173 unhighlightAccessible, 174 } = this.props; 175 176 if (!enabled) { 177 return Description(); 178 } 179 180 // Audit is currently running. 181 const isAuditing = !!auditing.length; 182 183 return LocalizationProvider( 184 { bundles: fluentBundles }, 185 div( 186 { className: "mainFrame", role: "presentation", tabIndex: "-1" }, 187 Toolbar({ 188 audit, 189 simulate, 190 toolboxDoc: toolbox.doc, 191 }), 192 isAuditing && AuditProgressOverlay(), 193 span( 194 { 195 "aria-hidden": isAuditing, 196 role: "presentation", 197 style: { display: "contents" }, 198 }, 199 SplitBox({ 200 ref: "splitBox", 201 initialSize: SIDEBAR_WIDTH, 202 minSize: "10%", 203 maxSize: "80%", 204 splitterSize: 1, 205 endPanelControl: true, 206 startPanel: div( 207 { 208 className: "main-panel", 209 role: "presentation", 210 tabIndex: "-1", 211 }, 212 AccessibilityTree({ 213 toolboxDoc: toolbox.doc, 214 getAccessibilityTreeRoot, 215 startListeningForAccessibilityEvents, 216 stopListeningForAccessibilityEvents, 217 highlightAccessible, 218 unhighlightAccessible, 219 }) 220 ), 221 endPanel: RightSidebar({ 222 highlightAccessible, 223 unhighlightAccessible, 224 toolbox, 225 }), 226 vert: this.useLandscapeMode, 227 }) 228 ) 229 ) 230 ); 231 } 232 } 233 234 const mapStateToProps = ({ 235 ui: { enabled, supports }, 236 audit: { auditing }, 237 }) => ({ 238 enabled, 239 supports, 240 auditing, 241 }); 242 243 // Exports from this module 244 module.exports = connect(mapStateToProps)(MainFrame);