Source code
Revision control
Copy as Markdown
Other Tools
/* 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
"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 { hr } = dom;
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
const {
getStr,
} = require("resource://devtools/client/responsive/utils/l10n.js");
const {
parseUserAgent,
} = require("resource://devtools/client/responsive/utils/ua.js");
const Types = require("resource://devtools/client/responsive/types.js");
const MenuButton = createFactory(
require("resource://devtools/client/shared/components/menu/MenuButton.js")
);
loader.lazyGetter(this, "MenuItem", () => {
const menuItemClass = require("resource://devtools/client/shared/components/menu/MenuItem.js");
const menuItem = createFactory(menuItemClass);
menuItem.DUMMY_ICON = menuItemClass.DUMMY_ICON;
return menuItem;
});
loader.lazyGetter(this, "MenuList", () => {
return createFactory(
require("resource://devtools/client/shared/components/menu/MenuList.js")
);
});
class DeviceSelector extends PureComponent {
static get propTypes() {
return {
devices: PropTypes.shape(Types.devices).isRequired,
onChangeDevice: PropTypes.func.isRequired,
onUpdateDeviceModal: PropTypes.func.isRequired,
selectedDevice: PropTypes.string.isRequired,
viewportId: PropTypes.number.isRequired,
};
}
getMenuProps(device) {
if (!device) {
return { icon: null, label: null, tooltip: null };
}
const { browser, os } = parseUserAgent(device.userAgent);
let label = device.name;
if (os) {
label += ` ${os.name}`;
if (os.version) {
label += ` ${os.version}`;
}
}
let icon = null;
let tooltip = label;
if (browser) {
tooltip += ` ${browser.name} ${browser.version}`;
}
return { icon, label, tooltip };
}
getSelectedDevice() {
const { devices, selectedDevice } = this.props;
if (!selectedDevice) {
return null;
}
for (const type of devices.types) {
for (const device of devices[type]) {
if (selectedDevice === device.name) {
return device;
}
}
}
return null;
}
renderMenuList() {
const {
devices,
onChangeDevice,
onUpdateDeviceModal,
selectedDevice,
viewportId,
} = this.props;
const menuItems = [];
for (const type of devices.types) {
for (const device of devices[type]) {
if (device.displayed) {
const { icon, label, tooltip } = this.getMenuProps(device);
menuItems.push(
MenuItem({
key: label,
className: "device-selector-item",
checked: selectedDevice === device.name,
label,
icon: icon || MenuItem.DUMMY_ICON,
tooltip,
onClick: () => onChangeDevice(viewportId, device, type),
})
);
}
}
}
menuItems.sort(function (a, b) {
return a.props.label.localeCompare(b.props.label);
});
if (menuItems.length) {
menuItems.push(hr({ key: "separator" }));
}
menuItems.push(
MenuItem({
key: "edit-device",
label: getStr("responsive.editDeviceList2"),
onClick: () => onUpdateDeviceModal(true, viewportId),
})
);
return MenuList({}, menuItems);
}
render() {
const { devices } = this.props;
const selectedDevice = this.getSelectedDevice();
let { icon, label, tooltip } = this.getMenuProps(selectedDevice);
if (!selectedDevice) {
label = getStr("responsive.responsiveMode");
}
// MenuButton is expected to be used in the toolbox document usually,
// but since RDM's frame also loads theme-switching.js, we can create
// MenuButtons (& HTMLTooltips) in the RDM frame document.
const toolboxDoc = window.document;
return MenuButton(
{
id: "device-selector",
menuId: "device-selector-menu",
toolboxDoc,
className: "devtools-button devtools-dropdown-button",
label,
icon,
title: tooltip,
disabled: devices.listState !== Types.loadableState.LOADED,
},
() => this.renderMenuList()
);
}
}
module.exports = DeviceSelector;