diff options
Diffstat (limited to 'devtools/client/responsive/components/DeviceModal.js')
-rw-r--r-- | devtools/client/responsive/components/DeviceModal.js | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/devtools/client/responsive/components/DeviceModal.js b/devtools/client/responsive/components/DeviceModal.js new file mode 100644 index 0000000000..1cd21951e3 --- /dev/null +++ b/devtools/client/responsive/components/DeviceModal.js @@ -0,0 +1,303 @@ +/* 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-env browser */ + +"use strict"; + +const { + createFactory, + PureComponent, +} = 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 DeviceForm = createFactory( + require("resource://devtools/client/responsive/components/DeviceForm.js") +); +const DeviceList = createFactory( + require("resource://devtools/client/responsive/components/DeviceList.js") +); + +const { + getFormatStr, + getStr, +} = require("resource://devtools/client/responsive/utils/l10n.js"); +const { + getDeviceString, +} = require("resource://devtools/client/shared/devices.js"); +const Types = require("resource://devtools/client/responsive/types.js"); + +class DeviceModal extends PureComponent { + static get propTypes() { + return { + deviceAdderViewportTemplate: PropTypes.shape(Types.viewport).isRequired, + devices: PropTypes.shape(Types.devices).isRequired, + onAddCustomDevice: PropTypes.func.isRequired, + onDeviceListUpdate: PropTypes.func.isRequired, + onEditCustomDevice: PropTypes.func.isRequired, + onRemoveCustomDevice: PropTypes.func.isRequired, + onUpdateDeviceDisplayed: PropTypes.func.isRequired, + onUpdateDeviceModal: PropTypes.func.isRequired, + }; + } + + constructor(props) { + super(props); + + this.state = { + // The device form type can be 3 states: "add", "edit", or "". + // "add" - The form shown is adding a new device. + // "edit" - The form shown is editing an existing custom device. + // "" - The form is closed. + deviceFormType: "", + // The device being edited from the edit form. + editingDevice: null, + }; + for (const type of this.props.devices.types) { + for (const device of this.props.devices[type]) { + this.state[device.name] = device.displayed; + } + } + + this.onAddCustomDevice = this.onAddCustomDevice.bind(this); + this.onDeviceCheckboxChange = this.onDeviceCheckboxChange.bind(this); + this.onDeviceFormShow = this.onDeviceFormShow.bind(this); + this.onDeviceFormHide = this.onDeviceFormHide.bind(this); + this.onDeviceModalSubmit = this.onDeviceModalSubmit.bind(this); + this.onEditCustomDevice = this.onEditCustomDevice.bind(this); + this.onKeyDown = this.onKeyDown.bind(this); + } + + componentDidMount() { + window.addEventListener("keydown", this.onKeyDown, true); + } + + componentWillUnmount() { + this.onDeviceModalSubmit(); + window.removeEventListener("keydown", this.onKeyDown, true); + } + + onAddCustomDevice(device) { + this.props.onAddCustomDevice(device); + // Default custom devices to enabled + this.setState({ + [device.name]: true, + }); + } + + onDeviceCheckboxChange({ nativeEvent: { button }, target }) { + if (button !== 0) { + return; + } + this.setState({ + [target.value]: !this.state[target.value], + }); + } + + onDeviceFormShow(type, device) { + this.setState({ + deviceFormType: type, + editingDevice: device, + }); + } + + onDeviceFormHide() { + this.setState({ + deviceFormType: "", + editingDevice: null, + }); + } + + onDeviceModalSubmit() { + const { devices, onDeviceListUpdate, onUpdateDeviceDisplayed } = this.props; + + const preferredDevices = { + added: new Set(), + removed: new Set(), + }; + + for (const type of devices.types) { + for (const device of devices[type]) { + const newState = this.state[device.name]; + + if (device.featured && !newState) { + preferredDevices.removed.add(device.name); + } else if (!device.featured && newState) { + preferredDevices.added.add(device.name); + } + + if (this.state[device.name] != device.displayed) { + onUpdateDeviceDisplayed(device, type, this.state[device.name]); + } + } + } + + onDeviceListUpdate(preferredDevices); + } + + onEditCustomDevice(newDevice) { + this.props.onEditCustomDevice(this.state.editingDevice, newDevice); + + // We want to remove the original device name from state after editing, so create a + // new state setting the old key to null and the new one to true. + this.setState({ + [this.state.editingDevice.name]: null, + [newDevice.name]: true, + }); + } + + onKeyDown(event) { + if (!this.props.devices.isModalOpen) { + return; + } + // Escape keycode + if (event.keyCode === 27) { + const { onUpdateDeviceModal } = this.props; + onUpdateDeviceModal(false); + } + } + + renderAddForm() { + // If a device is currently selected, fold its attributes into a single object for use + // as the starting values of the form. If no device is selected, use the values for + // the current window. + const { deviceAdderViewportTemplate: viewportTemplate } = this.props; + const deviceTemplate = this.props.deviceAdderViewportTemplate; + if (viewportTemplate.device) { + const device = this.props.devices[viewportTemplate.deviceType].find(d => { + return d.name == viewportTemplate.device; + }); + Object.assign(deviceTemplate, { + pixelRatio: device.pixelRatio, + userAgent: device.userAgent, + touch: device.touch, + name: getFormatStr("responsive.customDeviceNameFromBase", device.name), + }); + } else { + Object.assign(deviceTemplate, { + pixelRatio: window.devicePixelRatio, + userAgent: navigator.userAgent, + touch: false, + name: getStr("responsive.customDeviceName"), + }); + } + + return DeviceForm({ + formType: "add", + device: deviceTemplate, + devices: this.props.devices, + onDeviceFormHide: this.onDeviceFormHide, + onSave: this.onAddCustomDevice, + viewportTemplate, + }); + } + + renderDevices() { + const sortedDevices = {}; + for (const type of this.props.devices.types) { + sortedDevices[type] = this.props.devices[type].sort((a, b) => + a.name.localeCompare(b.name) + ); + + sortedDevices[type].forEach(device => { + device.isChecked = this.state[device.name]; + }); + } + + return this.props.devices.types.map(type => { + return sortedDevices[type].length + ? dom.div( + { + className: `device-type device-type-${type}`, + key: type, + }, + dom.header({ className: "device-header" }, getDeviceString(type)), + DeviceList({ + devices: sortedDevices, + isDeviceFormShown: this.state.deviceFormType, + type, + onDeviceCheckboxChange: this.onDeviceCheckboxChange, + onDeviceFormHide: this.onDeviceFormHide, + onDeviceFormShow: this.onDeviceFormShow, + onEditCustomDevice: this.onEditCustomDevice, + onRemoveCustomDevice: this.props.onRemoveCustomDevice, + }) + ) + : null; + }); + } + + renderEditForm() { + return DeviceForm({ + formType: "edit", + device: this.state.editingDevice, + devices: this.props.devices, + onDeviceFormHide: this.onDeviceFormHide, + onSave: this.onEditCustomDevice, + viewportTemplate: { + width: this.state.editingDevice.width, + height: this.state.editingDevice.height, + }, + }); + } + + renderForm() { + let form = null; + + if (this.state.deviceFormType === "add") { + form = this.renderAddForm(); + } else if (this.state.deviceFormType === "edit") { + form = this.renderEditForm(); + } + + return form; + } + + render() { + const { onUpdateDeviceModal } = this.props; + const isDeviceFormShown = this.state.deviceFormType; + + return dom.div( + { + id: "device-modal-wrapper", + className: this.props.devices.isModalOpen ? "opened" : "closed", + }, + dom.div( + { className: "device-modal" }, + dom.div( + { className: "device-modal-header" }, + !isDeviceFormShown + ? dom.header( + { className: "device-modal-title" }, + getStr("responsive.deviceSettings"), + dom.button({ + id: "device-close-button", + className: "devtools-button", + onClick: () => onUpdateDeviceModal(false), + }) + ) + : null, + !isDeviceFormShown + ? dom.button( + { + id: "device-add-button", + onClick: () => this.onDeviceFormShow("add"), + }, + getStr("responsive.addDevice2") + ) + : null, + this.renderForm() + ), + dom.div({ className: "device-modal-content" }, this.renderDevices()) + ), + dom.div({ + className: "modal-overlay", + onClick: () => onUpdateDeviceModal(false), + }) + ); + } +} + +module.exports = DeviceModal; |