Formatting fixed

This commit is contained in:
Manoj Vivek 2020-03-14 13:31:12 +05:30
parent a3340d43d8
commit e11664a60d
122 changed files with 8588 additions and 7241 deletions

View file

@ -1,13 +1,14 @@
{
"overrides": [
{
"files": [".prettierrc", ".babelrc", ".eslintrc", ".stylelintrc"],
"options": {
"parser": "json"
}
}
],
"singleQuote": true,
"bracketSpacing": false,
"trailingComma": "es5"
"overrides": [
{
"files": [".prettierrc", ".babelrc", ".eslintrc", ".stylelintrc"],
"options": {
"parser": "json"
}
}
],
"tabWidth": 4,
"singleQuote": true,
"bracketSpacing": false,
"trailingComma": "es5"
}

View file

@ -1,36 +1,36 @@
{
"editor.formatOnSave": true,
"files.associations": {
".babelrc": "jsonc",
".eslintrc": "jsonc",
".prettierrc": "jsonc",
"editor.formatOnSave": true,
"files.associations": {
".babelrc": "jsonc",
".eslintrc": "jsonc",
".prettierrc": "jsonc",
".stylelintrc": "json",
".stylelintrc": "json",
".dockerignore": "ignore",
".eslintignore": "ignore",
".flowconfig": "ignore"
},
".dockerignore": "ignore",
".eslintignore": "ignore",
".flowconfig": "ignore"
},
"javascript.validate.enable": false,
"javascript.format.enable": false,
"typescript.validate.enable": false,
"typescript.format.enable": false,
"javascript.validate.enable": false,
"javascript.format.enable": false,
"typescript.validate.enable": false,
"typescript.format.enable": false,
"flow.useNPMPackagedFlow": true,
"search.exclude": {
".git": true,
".eslintcache": true,
"app/dist": true,
"app/main.prod.js": true,
"app/main.prod.js.map": true,
"bower_components": true,
"dll": true,
"flow-typed": true,
"release": true,
"node_modules": true,
"npm-debug.log.*": true,
"test/**/__snapshots__": true,
"yarn.lock": true
}
"flow.useNPMPackagedFlow": true,
"search.exclude": {
".git": true,
".eslintcache": true,
"app/dist": true,
"app/main.prod.js": true,
"app/main.prod.js.map": true,
"bower_components": true,
"dll": true,
"flow-typed": true,
"release": true,
"node_modules": true,
"npm-debug.log.*": true,
"test/**/__snapshots__": true,
"yarn.lock": true
}
}

View file

@ -7,16 +7,16 @@ import LeftIconsPaneContainer from './containers/LeftIconsPaneContainer';
import styles from './layout.css';
export default () => (
<App>
<div className={styles.appRoot}>
<div className={styles.iconColumn}>
<LeftIconsPaneContainer />
</div>
<div className={styles.contentColumn}>
<Switch>
<Route path={routes.HOME} component={Browser} />
</Switch>
</div>
</div>
</App>
<App>
<div className={styles.appRoot}>
<div className={styles.iconColumn}>
<LeftIconsPaneContainer />
</div>
<div className={styles.contentColumn}>
<Switch>
<Route path={routes.HOME} component={Browser} />
</Switch>
</div>
</div>
</App>
);

View file

@ -2,14 +2,14 @@
import type {Dispatch, BrowserStateType} from '../reducers/types';
import pubsub from 'pubsub.js';
import {
SCROLL_DOWN,
SCROLL_UP,
NAVIGATION_BACK,
NAVIGATION_FORWARD,
NAVIGATION_RELOAD,
SCREENSHOT_ALL_DEVICES,
FLIP_ORIENTATION_ALL_DEVICES,
ENABLE_INSPECTOR_ALL_DEVICES,
SCROLL_DOWN,
SCROLL_UP,
NAVIGATION_BACK,
NAVIGATION_FORWARD,
NAVIGATION_RELOAD,
SCREENSHOT_ALL_DEVICES,
FLIP_ORIENTATION_ALL_DEVICES,
ENABLE_INSPECTOR_ALL_DEVICES,
} from '../constants/pubsubEvents';
export const NEW_ADDRESS = 'NEW_ADDRESS';
@ -24,320 +24,320 @@ export const NEW_ACTIVE_DEVICE = 'NEW_ACTIVE_DEVICE';
export const NEW_FILTERS = 'NEW_FILTERS';
export function newAddress(address) {
return {
type: NEW_ADDRESS,
address,
};
return {
type: NEW_ADDRESS,
address,
};
}
export function newHomepage(homepage) {
return {
type: NEW_HOMEPAGE,
homepage,
};
return {
type: NEW_HOMEPAGE,
homepage,
};
}
export function newZoomLevel(zoomLevel) {
return {
type: NEW_ZOOM_LEVEL,
zoomLevel,
};
return {
type: NEW_ZOOM_LEVEL,
zoomLevel,
};
}
export function newScrollPosition(scrollPosition) {
return {
type: NEW_SCROLL_POSITION,
scrollPosition,
};
return {
type: NEW_SCROLL_POSITION,
scrollPosition,
};
}
export function newNavigatorStatus(navigatorStatus) {
return {
type: NEW_NAVIGATOR_STATUS,
navigatorStatus,
};
return {
type: NEW_NAVIGATOR_STATUS,
navigatorStatus,
};
}
export function newDrawerContent(drawer) {
return {
type: NEW_DRAWER_CONTENT,
drawer,
};
return {
type: NEW_DRAWER_CONTENT,
drawer,
};
}
export function newPreviewerConfig(previewer) {
return {
type: NEW_PREVIEWER_CONFIG,
previewer,
};
return {
type: NEW_PREVIEWER_CONFIG,
previewer,
};
}
export function newActiveDevices(devices) {
return {
type: NEW_ACTIVE_DEVICES,
devices,
};
return {
type: NEW_ACTIVE_DEVICES,
devices,
};
}
export function newActiveDevice(device) {
return {
type: NEW_ACTIVE_DEVICE,
device,
};
return {
type: NEW_ACTIVE_DEVICE,
device,
};
}
export function newFilters(filters) {
return {
type: NEW_FILTERS,
filters,
};
return {
type: NEW_FILTERS,
filters,
};
}
export function onAddressChange(newURL) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {address},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {address},
} = getState();
console.log('newURL', newURL);
console.log('newURL', newURL);
if (newURL === address) {
return;
}
if (newURL === address) {
return;
}
dispatch(newAddress(newURL));
};
dispatch(newAddress(newURL));
};
}
export function onZoomChange(newLevel) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {zoomLevel},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {zoomLevel},
} = getState();
if (newLevel === zoomLevel) {
return;
}
if (newLevel === zoomLevel) {
return;
}
dispatch(newZoomLevel(newLevel));
};
dispatch(newZoomLevel(newLevel));
};
}
export function onScrollChange({x: newX, y: newY}) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {
scrollPosition: {x, y},
},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {
scrollPosition: {x, y},
},
} = getState();
if (newX === x && newY === y) {
return;
}
if (newX === x && newY === y) {
return;
}
dispatch(newScrollPosition({x: newX, y: newY}));
};
dispatch(newScrollPosition({x: newX, y: newY}));
};
}
export function updateNavigatorStatus({
backEnabled: newBackEnabled,
forwardEnabled: newForwardEnabled,
backEnabled: newBackEnabled,
forwardEnabled: newForwardEnabled,
}) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {
navigatorStatus: {backEnabled, forwardEnabled},
},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {
navigatorStatus: {backEnabled, forwardEnabled},
},
} = getState();
if (
newBackEnabled === backEnabled &&
newForwardEnabled === forwardEnabled
) {
return;
}
if (
newBackEnabled === backEnabled &&
newForwardEnabled === forwardEnabled
) {
return;
}
dispatch(
newNavigatorStatus({
backEnabled: newBackEnabled,
forwardEnabled: newForwardEnabled,
})
);
};
dispatch(
newNavigatorStatus({
backEnabled: newBackEnabled,
forwardEnabled: newForwardEnabled,
})
);
};
}
export function openDrawerAndSetContent(_newDrawerContent) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {
drawer: {drawerContent, open},
},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {
drawer: {drawerContent, open},
},
} = getState();
if (_newDrawerContent === drawerContent && open) {
return;
}
if (_newDrawerContent === drawerContent && open) {
return;
}
dispatch(
newDrawerContent({
content: _newDrawerContent,
open: true,
})
);
};
dispatch(
newDrawerContent({
content: _newDrawerContent,
open: true,
})
);
};
}
export function changeDrawerOpenState(newOpenState) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {drawer},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {drawer},
} = getState();
if (newOpenState === drawer.open) {
return;
}
if (newOpenState === drawer.open) {
return;
}
dispatch(
newDrawerContent({
...drawer,
open: newOpenState,
})
);
};
dispatch(
newDrawerContent({
...drawer,
open: newOpenState,
})
);
};
}
export function setPreviewLayout(newLayout) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {previewer},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {previewer},
} = getState();
if (previewer.layout === newLayout) {
return;
}
if (previewer.layout === newLayout) {
return;
}
dispatch(
newPreviewerConfig({
...previewer,
layout: newLayout,
})
);
};
dispatch(
newPreviewerConfig({
...previewer,
layout: newLayout,
})
);
};
}
export function setActiveDevices(newDevices) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {devices},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {devices},
} = getState();
if (false) {
//TODO verify the devices list and return if the order of the devices didn;t change;
return;
}
if (false) {
//TODO verify the devices list and return if the order of the devices didn;t change;
return;
}
dispatch(newActiveDevices(newDevices));
};
dispatch(newActiveDevices(newDevices));
};
}
export function addNewDevice(newDevice) {
return (dispatch: Dispatch, getState: RootStateType) => {
if (newDevice.added) {
dispatch(newActiveDevice(newDevice));
}
};
return (dispatch: Dispatch, getState: RootStateType) => {
if (newDevice.added) {
dispatch(newActiveDevice(newDevice));
}
};
}
export function toggleFilter(filterField, value) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {filters},
} = getState();
if (!filters[filterField]) {
filters[filterField] = [];
}
const index = filters[filterField].indexOf(value);
if (index === -1) {
filters[filterField].push(value);
} else {
filters[filterField].splice(index, 1);
}
dispatch(newFilters(filters));
};
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {filters},
} = getState();
if (!filters[filterField]) {
filters[filterField] = [];
}
const index = filters[filterField].indexOf(value);
if (index === -1) {
filters[filterField].push(value);
} else {
filters[filterField].splice(index, 1);
}
dispatch(newFilters(filters));
};
}
export function goToHomepage() {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {homepage, address},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {homepage, address},
} = getState();
if (homepage === address) {
return;
}
if (homepage === address) {
return;
}
dispatch(newAddress(homepage));
};
dispatch(newAddress(homepage));
};
}
export function setCurrentAddressAsHomepage() {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {homepage, address},
} = getState();
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {homepage, address},
} = getState();
if (homepage === address) {
return;
}
if (homepage === address) {
return;
}
dispatch(newHomepage(address));
};
dispatch(newHomepage(address));
};
}
export function triggerScrollDown() {
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(SCROLL_DOWN);
};
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(SCROLL_DOWN);
};
}
export function screenshotAllDevices() {
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(SCREENSHOT_ALL_DEVICES, [{now: new Date()}]);
};
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(SCREENSHOT_ALL_DEVICES, [{now: new Date()}]);
};
}
export function flipOrientationAllDevices() {
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(FLIP_ORIENTATION_ALL_DEVICES);
};
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(FLIP_ORIENTATION_ALL_DEVICES);
};
}
export function enableInpector() {
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(ENABLE_INSPECTOR_ALL_DEVICES);
};
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(ENABLE_INSPECTOR_ALL_DEVICES);
};
}
export function triggerScrollUp() {
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(SCROLL_UP);
};
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(SCROLL_UP);
};
}
export function triggerNavigationBack() {
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(NAVIGATION_BACK);
};
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(NAVIGATION_BACK);
};
}
export function triggerNavigationForward() {
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(NAVIGATION_FORWARD);
};
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(NAVIGATION_FORWARD);
};
}
export function triggerNavigationReload() {
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(NAVIGATION_RELOAD);
};
return (dispatch: Dispatch, getState: RootStateType) => {
pubsub.publish(NAVIGATION_RELOAD);
};
}

View file

@ -1,70 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<link
href="https://fonts.googleapis.com/css?family=Roboto&display=swap"
rel="stylesheet"
/>
<meta charset="utf-8" />
<title>Responsively</title>
<script type="text/javascript">
// prettier-ignore
window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=t.forceSSL||"https:"===document.location.protocol,a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=(r?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+e+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","resetIdentity","removeEventProperty","setEventProperties","track","unsetEventProperty"],c=0;c<p.length;c++)heap[p[c]]=o(p[c])};
heap.load('3453744655');
</script>
<script>
(function() {
if (!process.env.HOT) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = './dist/style.css';
// HACK: Writing the script path should be done with webpack
document.getElementsByTagName('head')[0].appendChild(link);
}
})();
</script>
</head>
<body>
<div id="root"></div>
<script>
{
const scripts = [];
<head>
<link
href="https://fonts.googleapis.com/css?family=Roboto&display=swap"
rel="stylesheet"
/>
<meta charset="utf-8" />
<title>Responsively</title>
<script type="text/javascript">
// prettier-ignore
window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=t.forceSSL||"https:"===document.location.protocol,a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=(r?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+e+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","resetIdentity","removeEventProperty","setEventProperties","track","unsetEventProperty"],c=0;c<p.length;c++)heap[p[c]]=o(p[c])};
heap.load('3453744655');
</script>
<script>
(function() {
if (!process.env.HOT) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = './dist/style.css';
// HACK: Writing the script path should be done with webpack
document.getElementsByTagName('head')[0].appendChild(link);
}
})();
</script>
</head>
<body>
<div id="root"></div>
<script>
{
const scripts = [];
// Dynamically insert the DLL script in development env in the
// renderer process
if (process.env.NODE_ENV === 'development') {
scripts.push('../dll/renderer.dev.dll.js');
}
// Dynamically insert the DLL script in development env in the
// renderer process
if (process.env.NODE_ENV === 'development') {
scripts.push('../dll/renderer.dev.dll.js');
}
// Dynamically insert the bundled app script in the renderer process
const port = process.env.PORT || 1212;
scripts.push(
process.env.HOT
? 'http://localhost:' + port + '/dist/renderer.dev.js'
: './dist/renderer.prod.js'
);
// Dynamically insert the bundled app script in the renderer process
const port = process.env.PORT || 1212;
scripts.push(
process.env.HOT
? 'http://localhost:' + port + '/dist/renderer.dev.js'
: './dist/renderer.prod.js'
);
document.write(
scripts
.map(script => `<script defer src="${script}"><\/script>`)
.join('')
);
}
</script>
<script>
// @see https://docs.headwayapp.co/widget for more configuration options.
var HW_config = {
selector: '#headway', // CSS selector where to inject the badge
account: 'xdYpn7',
};
setTimeout(() => {
var headwayScript = document.createElement('script');
headwayScript.setAttribute(
'src',
'https://cdn.headwayapp.co/widget.js'
);
document.head.appendChild(headwayScript);
}, 4000);
</script>
</body>
document.write(
scripts
.map(
script => `<script defer src="${script}"><\/script>`
)
.join('')
);
}
</script>
<script>
// @see https://docs.headwayapp.co/widget for more configuration options.
var HW_config = {
selector: '#headway', // CSS selector where to inject the badge
account: 'xdYpn7',
};
setTimeout(() => {
var headwayScript = document.createElement('script');
headwayScript.setAttribute(
'src',
'https://cdn.headwayapp.co/widget.js'
);
document.head.appendChild(headwayScript);
}, 4000);
</script>
</body>
</html>

View file

@ -10,97 +10,107 @@ import styles from './style.css';
import {Tooltip} from '@material-ui/core';
type Props = {
address: string,
onChange: () => void,
address: string,
onChange: () => void,
};
type State = {
address: string,
address: string,
};
class AddressBar extends React.Component<Props> {
props: Props;
state: State;
props: Props;
state: State;
constructor(props) {
super(props);
this.state = {
userTypedAddress: props.address,
previousAddress: props.address,
};
this.inputRef = React.createRef();
}
static getDerivedStateFromProps(props, state) {
if (props.address != state.previousAddress) {
return {userTypedAddress: props.address, previousAddress: props.address};
constructor(props) {
super(props);
this.state = {
userTypedAddress: props.address,
previousAddress: props.address,
};
this.inputRef = React.createRef();
}
return null;
}
render() {
return (
<div className={styles.addressBarContainer}>
<input
ref={this.inputRef}
type="text"
id="adress"
name="address"
className={styles.addressInput}
placeholder="https://your-website.com"
value={this.state.userTypedAddress}
onKeyDown={this._handleKeyDown}
onChange={e => this.setState({userTypedAddress: e.target.value})}
/>
<div
className={cx(styles.goButton, commonStyles.icons, {
[commonStyles.enabled]: this.props.address !== this.props.homepage,
[commonStyles.disabled]: this.props.address == this.props.homepage,
})}
>
<Tooltip title="Set a Homepage">
<div
className={cx(commonStyles.flexAlignVerticalMiddle)}
onClick={this.props.setHomepage}
>
<HomePlusIcon
height={22}
width={22}
color={iconsColor}
padding={5}
/>
static getDerivedStateFromProps(props, state) {
if (props.address != state.previousAddress) {
return {
userTypedAddress: props.address,
previousAddress: props.address,
};
}
return null;
}
render() {
return (
<div className={styles.addressBarContainer}>
<input
ref={this.inputRef}
type="text"
id="adress"
name="address"
className={styles.addressInput}
placeholder="https://your-website.com"
value={this.state.userTypedAddress}
onKeyDown={this._handleKeyDown}
onChange={e =>
this.setState({userTypedAddress: e.target.value})
}
/>
<div
className={cx(styles.goButton, commonStyles.icons, {
[commonStyles.enabled]:
this.props.address !== this.props.homepage,
[commonStyles.disabled]:
this.props.address == this.props.homepage,
})}
>
<Tooltip title="Set a Homepage">
<div
className={cx(commonStyles.flexAlignVerticalMiddle)}
onClick={this.props.setHomepage}
>
<HomePlusIcon
height={22}
width={22}
color={iconsColor}
padding={5}
/>
</div>
</Tooltip>
</div>
</div>
</Tooltip>
</div>
</div>
);
}
_handleKeyDown = e => {
if (e.key === 'Enter') {
this.inputRef.current.blur();
this._onChange();
);
}
};
_onChange = () => {
if (!this.state.userTypedAddress) {
return;
}
this.props.onChange &&
this.props.onChange(this._normalize(this.state.userTypedAddress));
};
_handleKeyDown = e => {
if (e.key === 'Enter') {
this.inputRef.current.blur();
this._onChange();
}
};
_normalize = address => {
if (!address.startsWith('http')) {
let protocol = 'https://';
if (address.startsWith('localhost') || address.startsWith('127.0.0.1')) {
protocol = 'http://';
}
address = `${protocol}${address}`;
}
return address;
};
_onChange = () => {
if (!this.state.userTypedAddress) {
return;
}
this.props.onChange &&
this.props.onChange(this._normalize(this.state.userTypedAddress));
};
_normalize = address => {
if (!address.startsWith('http')) {
let protocol = 'https://';
if (
address.startsWith('localhost') ||
address.startsWith('127.0.0.1')
) {
protocol = 'http://';
}
address = `${protocol}${address}`;
}
return address;
};
}
export default AddressBar;

View file

@ -12,14 +12,14 @@ import commonStyles from '../common.styles.css';
import DevicesOverviewContainer from '../../containers/DevicesOverviewContainer';
export default function DeviceDrawer(props) {
return (
<div>
<DevicesOverviewContainer />
<PreviewerLayoutSelector
value={props.browser.previewer.layout}
onChange={val => props.setPreviewLayout(val.value)}
/>
<QuickFilterDevicesContainer />
</div>
);
return (
<div>
<DevicesOverviewContainer />
<PreviewerLayoutSelector
value={props.browser.previewer.layout}
onChange={val => props.setPreviewLayout(val.value)}
/>
<QuickFilterDevicesContainer />
</div>
);
}

View file

@ -21,12 +21,12 @@ import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
//import Switch from '@material-ui/core/Switch';
import {
Typography,
Grid,
MenuItem,
NativeSelect,
RadioGroup,
Radio,
Typography,
Grid,
MenuItem,
NativeSelect,
RadioGroup,
Radio,
} from '@material-ui/core';
import RadioButtonUncheckedIcon from '@material-ui/icons/RadioButtonUnchecked';
import RadioButtonCheckedIcon from '@material-ui/icons/RadioButtonChecked';
@ -37,402 +37,426 @@ import {lightIconsColor, themeColor} from '../../../constants/colors';
import Switch from 'react-switch';
const useStyles = makeStyles(theme => ({
fab: {
position: 'absolute',
top: theme.spacing(10),
right: theme.spacing(3),
},
extendedIcon: {
marginRight: theme.spacing(1),
},
inputField: {
marginRight: '1%',
width: '49%',
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
select: {
marginTop: theme.spacing(2),
maxWidth: 150,
},
radioIcon: {
color: lightIconsColor,
},
inputAdornment: {
color: lightIconsColor,
},
userAgent: {
fontSize: 12,
},
actionButton: {
padding: '10px !important',
borderRadius: '5px !important',
},
fab: {
position: 'absolute',
top: theme.spacing(10),
right: theme.spacing(3),
},
extendedIcon: {
marginRight: theme.spacing(1),
},
inputField: {
marginRight: '1%',
width: '49%',
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
select: {
marginTop: theme.spacing(2),
maxWidth: 150,
},
radioIcon: {
color: lightIconsColor,
},
inputAdornment: {
color: lightIconsColor,
},
userAgent: {
fontSize: 12,
},
actionButton: {
padding: '10px !important',
borderRadius: '5px !important',
},
}));
export default function AddDevice(props) {
const [open, setOpen] = useState(false);
const [name, setName] = useState('');
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const [userAgentString, setUserAgentString] = useState('');
const [previewState, setPreviewState] = useState(true);
const [capabilities, setCapabilities] = useState({
[CAPABILITIES.mobile]: false,
[CAPABILITIES.touch]: true,
});
const [deviceType, setDeviceType] = useState(DEVICE_TYPE.phone);
const [os, setOS] = useState(OS.android);
const classes = useStyles();
const updateUserAgent = () => {
console.log(os, deviceType);
let userAgent =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36';
if (os === OS.android) {
if (deviceType === DEVICE_TYPE.phone) {
userAgent =
'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36';
}
if (deviceType === DEVICE_TYPE.tablet) {
userAgent =
'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36';
}
}
if (os === OS.ios) {
if (deviceType === DEVICE_TYPE.phone) {
userAgent =
'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1';
}
if (deviceType === DEVICE_TYPE.tablet) {
userAgent =
'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1';
}
}
if (os === OS.windowsPhone) {
userAgent =
'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)';
}
setUserAgentString(userAgent);
};
React.useEffect(updateUserAgent, [os, deviceType]);
const CustomRadio = props => (
<Radio
{...props}
color="primary"
icon={<RadioButtonUncheckedIcon className={classes.radioIcon} />}
checkedIcon={<RadioButtonCheckedIcon className={classes.radioIcon} />}
/>
);
const CustomCheckbox = props => (
<Checkbox
{...props}
color="primary"
icon={<CheckBoxOutlineBlankIcon className={classes.radioIcon} />}
checkedIcon={<CheckBoxIcon className={classes.radioIcon} />}
/>
);
const CustomSwitch = withStyles({
switchBase: {
color: lightIconsColor,
'&$checked': {
color: lightIconsColor,
},
'&$checked + $track': {
backgroundColor: lightIconsColor,
},
},
checked: {},
track: {},
})(Switch);
const closeDialog = () => setOpen(false);
const saveDevice = () => {
props.addNewDevice({
name,
width: parseInt(width, 10),
height: parseInt(height, 10),
added: previewState,
useragent: userAgentString,
id: `custom-${new Date().getTime()}`,
type: deviceType,
capabilities: Object.keys(capabilities).filter(val => capabilities[val]),
os,
const [open, setOpen] = useState(false);
const [name, setName] = useState('');
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const [userAgentString, setUserAgentString] = useState('');
const [previewState, setPreviewState] = useState(true);
const [capabilities, setCapabilities] = useState({
[CAPABILITIES.mobile]: false,
[CAPABILITIES.touch]: true,
});
closeDialog();
};
console.log('capabilities', capabilities);
return (
<Fragment>
<Fab
variant="extended"
aria-label="add"
color="primary"
className={classes.fab}
onClick={() => setOpen(true)}
>
<AddIcon className={classes.extendedIcon} /> New Device
</Fab>
const [deviceType, setDeviceType] = useState(DEVICE_TYPE.phone);
const [os, setOS] = useState(OS.android);
const classes = useStyles();
<Dialog
disableEnforceFocus
open={open}
onClose={closeDialog}
aria-labelledby="form-dialog-title"
fullWidth={true}
>
<DialogTitle id="form-dialog-title">New Device</DialogTitle>
<DialogContent>
<DialogContentText color="ternary">
Please enter the details of the new device
</DialogContentText>
<TextField
autoFocus
fullWidth
variant="outlined"
margin="normal"
label="Name"
type="text"
placeholder="Device name"
InputLabelProps={{
shrink: true,
}}
value={name}
onChange={e => setName(e.target.value)}
className={'padded-input'}
/>
<TextField
className={classes.inputField}
variant="outlined"
margin="normal"
label="Width"
type="number"
InputProps={{
endAdornment: (
<InputAdornment position="end" className="input-adornment">
px
</InputAdornment>
),
}}
value={width}
onChange={e => setWidth(e.target.value)}
className={'padded-input'}
/>
<TextField
className={classes.inputField}
variant="outlined"
margin="normal"
label="Height"
type="number"
InputProps={{
endAdornment: (
<InputAdornment position="end" className="input-adornment">
px
</InputAdornment>
),
}}
value={height}
onChange={e => setHeight(e.target.value)}
className={'padded-input'}
/>
<FormControl
component="fieldset"
className={classes.formControl}
fullWidth
>
<FormLabel component="legend">Device Type</FormLabel>
<RadioGroup
name="deviceType"
value={deviceType}
onChange={e => setDeviceType(e.target.value)}
>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<FormControlLabel
value={DEVICE_TYPE.phone}
control={<CustomRadio />}
label="Phone"
/>
</Grid>
<Grid item>
<FormControlLabel
value={DEVICE_TYPE.tablet}
control={<CustomRadio />}
label="Tablet"
/>
</Grid>
<Grid item>
<FormControlLabel
value={DEVICE_TYPE.desktop}
control={<CustomRadio />}
label="Laptop/Desktop"
/>
</Grid>
</Grid>
</RadioGroup>
</FormControl>
<FormControl
component="fieldset"
className={classes.formControl}
fullWidth
>
<FormLabel component="legend">Operating System</FormLabel>
<RadioGroup
name="os"
value={os}
onChange={e => setOS(e.target.value)}
>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<FormControlLabel
value={OS.android}
control={<CustomRadio />}
label="Android"
/>
</Grid>
<Grid item>
<FormControlLabel
value={OS.ios}
control={<CustomRadio />}
label="iOS"
/>
</Grid>
<Grid item>
<FormControlLabel
value={OS.windowsPhone}
control={<CustomRadio />}
label="Windows Phone"
/>
</Grid>
<Grid item>
<FormControlLabel
value={OS.pc}
control={<CustomRadio />}
label="Desktop"
/>
</Grid>
</Grid>
</RadioGroup>
</FormControl>
<FormLabel>Device capabilities</FormLabel>
<FormGroup>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<FormControlLabel
control={
<CustomCheckbox
checked={capabilities[CAPABILITIES.mobile]}
onChange={e =>
setCapabilities({
...capabilities,
[CAPABILITIES.mobile]: e.target.checked,
})
}
value="Flippable"
/>
}
label="Flippable"
/>
</Grid>
<Grid item>
<FormControlLabel
control={
<CustomCheckbox
checked={capabilities[CAPABILITIES.touch]}
onChange={e =>
setCapabilities({
...capabilities,
[CAPABILITIES.touch]: e.target.checked,
})
}
value="Touchscreen"
/>
}
label="Touchscreen"
/>
</Grid>
</Grid>
</FormGroup>
<TextField
fullWidth
variant="outlined"
margin="normal"
label="User-Agent String"
placeholder="User-Agent String"
InputLabelProps={{
shrink: true,
}}
InputProps={{
classes: {
input: classes.userAgent,
},
}}
type="text"
value={userAgentString}
onChange={e => setUserAgentString(e.target.value)}
className={cx('padded-input')}
/>
const updateUserAgent = () => {
console.log(os, deviceType);
let userAgent =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36';
if (os === OS.android) {
if (deviceType === DEVICE_TYPE.phone) {
userAgent =
'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36';
}
if (deviceType === DEVICE_TYPE.tablet) {
userAgent =
'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36';
}
}
if (os === OS.ios) {
if (deviceType === DEVICE_TYPE.phone) {
userAgent =
'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1';
}
if (deviceType === DEVICE_TYPE.tablet) {
userAgent =
'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1';
}
}
<Grid
container
alignItems="center"
className="MuiFormControl-marginNormal"
>
<Grid item style={{flex: 1}} className="MuiFormLabel-root">
Activate Preview
</Grid>
<Grid
item
className={cx('MuiFormLabel-root', {
'Mui-focused': !previewState,
})}
>
{/*Off*/}
</Grid>
<Grid item>
<Switch
onChange={checked => setPreviewState(checked)}
checked={previewState}
onColor={themeColor}
/>
</Grid>
<Grid
item
className={cx('MuiFormLabel-root', {
'Mui-focused': previewState,
})}
>
{/*On*/}
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button
onClick={closeDialog}
if (os === OS.windowsPhone) {
userAgent =
'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)';
}
setUserAgentString(userAgent);
};
React.useEffect(updateUserAgent, [os, deviceType]);
const CustomRadio = props => (
<Radio
{...props}
color="primary"
className={classes.actionButton}
>
Cancel
</Button>
<Button
variant="contained"
onClick={saveDevice}
icon={<RadioButtonUncheckedIcon className={classes.radioIcon} />}
checkedIcon={
<RadioButtonCheckedIcon className={classes.radioIcon} />
}
/>
);
const CustomCheckbox = props => (
<Checkbox
{...props}
color="primary"
className={classes.actionButton}
>
Add
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
icon={<CheckBoxOutlineBlankIcon className={classes.radioIcon} />}
checkedIcon={<CheckBoxIcon className={classes.radioIcon} />}
/>
);
const CustomSwitch = withStyles({
switchBase: {
color: lightIconsColor,
'&$checked': {
color: lightIconsColor,
},
'&$checked + $track': {
backgroundColor: lightIconsColor,
},
},
checked: {},
track: {},
})(Switch);
const closeDialog = () => setOpen(false);
const saveDevice = () => {
props.addNewDevice({
name,
width: parseInt(width, 10),
height: parseInt(height, 10),
added: previewState,
useragent: userAgentString,
id: `custom-${new Date().getTime()}`,
type: deviceType,
capabilities: Object.keys(capabilities).filter(
val => capabilities[val]
),
os,
});
closeDialog();
};
console.log('capabilities', capabilities);
return (
<Fragment>
<Fab
variant="extended"
aria-label="add"
color="primary"
className={classes.fab}
onClick={() => setOpen(true)}
>
<AddIcon className={classes.extendedIcon} /> New Device
</Fab>
<Dialog
disableEnforceFocus
open={open}
onClose={closeDialog}
aria-labelledby="form-dialog-title"
fullWidth={true}
>
<DialogTitle id="form-dialog-title">New Device</DialogTitle>
<DialogContent>
<DialogContentText color="ternary">
Please enter the details of the new device
</DialogContentText>
<TextField
autoFocus
fullWidth
variant="outlined"
margin="normal"
label="Name"
type="text"
placeholder="Device name"
InputLabelProps={{
shrink: true,
}}
value={name}
onChange={e => setName(e.target.value)}
className={'padded-input'}
/>
<TextField
className={classes.inputField}
variant="outlined"
margin="normal"
label="Width"
type="number"
InputProps={{
endAdornment: (
<InputAdornment
position="end"
className="input-adornment"
>
px
</InputAdornment>
),
}}
value={width}
onChange={e => setWidth(e.target.value)}
className={'padded-input'}
/>
<TextField
className={classes.inputField}
variant="outlined"
margin="normal"
label="Height"
type="number"
InputProps={{
endAdornment: (
<InputAdornment
position="end"
className="input-adornment"
>
px
</InputAdornment>
),
}}
value={height}
onChange={e => setHeight(e.target.value)}
className={'padded-input'}
/>
<FormControl
component="fieldset"
className={classes.formControl}
fullWidth
>
<FormLabel component="legend">Device Type</FormLabel>
<RadioGroup
name="deviceType"
value={deviceType}
onChange={e => setDeviceType(e.target.value)}
>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<FormControlLabel
value={DEVICE_TYPE.phone}
control={<CustomRadio />}
label="Phone"
/>
</Grid>
<Grid item>
<FormControlLabel
value={DEVICE_TYPE.tablet}
control={<CustomRadio />}
label="Tablet"
/>
</Grid>
<Grid item>
<FormControlLabel
value={DEVICE_TYPE.desktop}
control={<CustomRadio />}
label="Laptop/Desktop"
/>
</Grid>
</Grid>
</RadioGroup>
</FormControl>
<FormControl
component="fieldset"
className={classes.formControl}
fullWidth
>
<FormLabel component="legend">
Operating System
</FormLabel>
<RadioGroup
name="os"
value={os}
onChange={e => setOS(e.target.value)}
>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<FormControlLabel
value={OS.android}
control={<CustomRadio />}
label="Android"
/>
</Grid>
<Grid item>
<FormControlLabel
value={OS.ios}
control={<CustomRadio />}
label="iOS"
/>
</Grid>
<Grid item>
<FormControlLabel
value={OS.windowsPhone}
control={<CustomRadio />}
label="Windows Phone"
/>
</Grid>
<Grid item>
<FormControlLabel
value={OS.pc}
control={<CustomRadio />}
label="Desktop"
/>
</Grid>
</Grid>
</RadioGroup>
</FormControl>
<FormLabel>Device capabilities</FormLabel>
<FormGroup>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<FormControlLabel
control={
<CustomCheckbox
checked={
capabilities[
CAPABILITIES.mobile
]
}
onChange={e =>
setCapabilities({
...capabilities,
[CAPABILITIES.mobile]:
e.target.checked,
})
}
value="Flippable"
/>
}
label="Flippable"
/>
</Grid>
<Grid item>
<FormControlLabel
control={
<CustomCheckbox
checked={
capabilities[CAPABILITIES.touch]
}
onChange={e =>
setCapabilities({
...capabilities,
[CAPABILITIES.touch]:
e.target.checked,
})
}
value="Touchscreen"
/>
}
label="Touchscreen"
/>
</Grid>
</Grid>
</FormGroup>
<TextField
fullWidth
variant="outlined"
margin="normal"
label="User-Agent String"
placeholder="User-Agent String"
InputLabelProps={{
shrink: true,
}}
InputProps={{
classes: {
input: classes.userAgent,
},
}}
type="text"
value={userAgentString}
onChange={e => setUserAgentString(e.target.value)}
className={cx('padded-input')}
/>
<Grid
container
alignItems="center"
className="MuiFormControl-marginNormal"
>
<Grid
item
style={{flex: 1}}
className="MuiFormLabel-root"
>
Activate Preview
</Grid>
<Grid
item
className={cx('MuiFormLabel-root', {
'Mui-focused': !previewState,
})}
>
{/*Off*/}
</Grid>
<Grid item>
<Switch
onChange={checked => setPreviewState(checked)}
checked={previewState}
onColor={themeColor}
/>
</Grid>
<Grid
item
className={cx('MuiFormLabel-root', {
'Mui-focused': previewState,
})}
>
{/*On*/}
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button
onClick={closeDialog}
color="primary"
className={classes.actionButton}
>
Cancel
</Button>
<Button
variant="contained"
onClick={saveDevice}
color="primary"
className={classes.actionButton}
>
Add
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
}

View file

@ -10,40 +10,44 @@ import {DEVICE_TYPE} from '../../../constants/devices';
import {getDeviceIcon, getOSIcon} from '../../../utils/iconUtils';
export default function DeviceItem({device, index}) {
return (
<Draggable draggableId={device.id} index={index}>
{provided => (
<div
className={cx(styles.deviceHolder)}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div
className={cx(
commonStyles.flexAlignVerticalMiddle,
commonStyles.fullWidth
return (
<Draggable draggableId={device.id} index={index}>
{provided => (
<div
className={cx(styles.deviceHolder)}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div
className={cx(
commonStyles.flexAlignVerticalMiddle,
commonStyles.fullWidth
)}
>
<DragIndicator style={{color: 'grey'}} />
<div
className={cx(
commonStyles.flexContainerSpaceBetween,
commonStyles.fullWidth
)}
>
<div
className={commonStyles.flexAlignVerticalMiddle}
>
{getDeviceIcon(device.type)}
<span className={styles.deviceName}>
{device.name}
</span>
<span className={styles.deviceDimensions}>
{device.width}x{device.height}
</span>
</div>
<div>{getOSIcon(device.os, '#ffffff90')}</div>
</div>
</div>
</div>
)}
>
<DragIndicator style={{color: 'grey'}} />
<div
className={cx(
commonStyles.flexContainerSpaceBetween,
commonStyles.fullWidth
)}
>
<div className={commonStyles.flexAlignVerticalMiddle}>
{getDeviceIcon(device.type)}
<span className={styles.deviceName}>{device.name}</span>
<span className={styles.deviceDimensions}>
{device.width}x{device.height}
</span>
</div>
<div>{getOSIcon(device.os, '#ffffff90')}</div>
</div>
</div>
</div>
)}
</Draggable>
);
</Draggable>
);
}

View file

@ -6,19 +6,23 @@ import cx from 'classnames';
import styles from './styles.css';
export default function DeviceList({droppableId, devices}) {
return (
<Droppable droppableId={droppableId}>
{provided => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={cx(styles.listHolder)}
>
{devices.map((device, index) => (
<DeviceItem device={device} index={index} key={device.id} />
))}
</div>
)}
</Droppable>
);
return (
<Droppable droppableId={droppableId}>
{provided => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={cx(styles.listHolder)}
>
{devices.map((device, index) => (
<DeviceItem
device={device}
index={index}
key={device.id}
/>
))}
</div>
)}
</Droppable>
);
}

View file

@ -21,110 +21,120 @@ import ErrorBoundary from '../ErrorBoundary';
import styles from './styles.css';
const useStyles = makeStyles(theme => ({
appBar: {
position: 'relative',
},
title: {
marginLeft: theme.spacing(2),
flex: 1,
},
appBar: {
position: 'relative',
},
title: {
marginLeft: theme.spacing(2),
flex: 1,
},
}));
export default function DeviceManager(props) {
const [open, setOpen] = useState(false);
const classes = useStyles();
const [open, setOpen] = useState(false);
const classes = useStyles();
const [devices, setDevices] = useState({
active: [],
inactive: [],
});
const [devices, setDevices] = useState({
active: [],
inactive: [],
});
useEffect(
() => {
const activeDevices = props.browser.devices;
const activeDevicesById = activeDevices.reduce((acc, val) => {
acc[val.id] = val;
return acc;
}, {});
const inactiveDevices = allDevices.filter(
device => !activeDevicesById[device.id]
);
setDevices({active: activeDevices, inactive: inactiveDevices});
},
[props.browser.devices.map(JSON.stringify).join(',')]
);
useEffect(
() => {
const activeDevices = props.browser.devices;
const activeDevicesById = activeDevices.reduce((acc, val) => {
acc[val.id] = val;
return acc;
}, {});
const inactiveDevices = allDevices.filter(
device => !activeDevicesById[device.id]
);
setDevices({active: activeDevices, inactive: inactiveDevices});
},
[props.browser.devices.map(JSON.stringify).join(',')]
);
const closeDialog = () => setOpen(false);
const closeDialog = () => setOpen(false);
const onDragEnd = result => {
const {source, destination} = result;
const onDragEnd = result => {
const {source, destination} = result;
const sourceList = devices[source.droppableId];
const destinationList = devices[destination.droppableId];
const sourceList = devices[source.droppableId];
const destinationList = devices[destination.droppableId];
const itemDragged = sourceList[source.index];
sourceList.splice(source.index, 1);
const itemDragged = sourceList[source.index];
sourceList.splice(source.index, 1);
destinationList.splice(destination.index, 0, itemDragged);
destinationList.splice(destination.index, 0, itemDragged);
setDevices({...devices});
props.setActiveDevices(devices.active);
};
return (
<Fragment>
<Button
variant="contained"
color="primary"
aria-label="upload picture"
component="span"
onClick={() => setOpen(true)}
className={styles.editButton}
>
Customize
{/*<EditIcon style={{fontSize: 'inherit'}} />*/}
</Button>
<Dialog fullScreen open={open} onClose={closeDialog}>
<AppBar className={classes.appBar} color="secondary">
<Toolbar>
{/*<IconButton edge="start" onClick={closeDialog} aria-label="close">
setDevices({...devices});
props.setActiveDevices(devices.active);
};
return (
<Fragment>
<Button
variant="contained"
color="primary"
aria-label="upload picture"
component="span"
onClick={() => setOpen(true)}
className={styles.editButton}
>
Customize
{/*<EditIcon style={{fontSize: 'inherit'}} />*/}
</Button>
<Dialog fullScreen open={open} onClose={closeDialog}>
<AppBar className={classes.appBar} color="secondary">
<Toolbar>
{/*<IconButton edge="start" onClick={closeDialog} aria-label="close">
<CloseIcon />
</IconButton>*/}
<Typography variant="h6" className={classes.title}>
Manage Devices
</Typography>
<Button color="inherit" onClick={closeDialog}>
close
</Button>
</Toolbar>
</AppBar>
<div className={styles.container}>
<p className={styles.toolTip}>
<span></span>Drag and drop the devices across to re-order them.
</p>
<DragDropContext onDragEnd={onDragEnd}>
<Grid container spacing={3} className={styles.content}>
<Grid item xs={3} className={styles.section}>
<div className={styles.listTitle}>
<LightBulbIcon height={30} color="#FFD517" />
Active Devices
<Typography variant="h6" className={classes.title}>
Manage Devices
</Typography>
<Button color="inherit" onClick={closeDialog}>
close
</Button>
</Toolbar>
</AppBar>
<div className={styles.container}>
<p className={styles.toolTip}>
<span></span>Drag and drop the devices across to
re-order them.
</p>
<DragDropContext onDragEnd={onDragEnd}>
<Grid container spacing={3} className={styles.content}>
<Grid item xs={3} className={styles.section}>
<div className={styles.listTitle}>
<LightBulbIcon
height={30}
color="#FFD517"
/>
Active Devices
</div>
<DeviceList
droppableId={'active'}
devices={devices.active}
/>
</Grid>
<Grid item xs={3} className={styles.section}>
<div className={styles.listTitle}>
<LightBulbIcon
height={30}
color="darkgrey"
/>
Inactive Devices
</div>
<DeviceList
droppableId={'inactive'}
devices={devices.inactive}
/>
</Grid>
</Grid>
</DragDropContext>
<AddDeviceContainer />
</div>
<DeviceList droppableId={'active'} devices={devices.active} />
</Grid>
<Grid item xs={3} className={styles.section}>
<div className={styles.listTitle}>
<LightBulbIcon height={30} color="darkgrey" />
Inactive Devices
</div>
<DeviceList
droppableId={'inactive'}
devices={devices.inactive}
/>
</Grid>
</Grid>
</DragDropContext>
<AddDeviceContainer />
</div>
</Dialog>
</Fragment>
);
</Dialog>
</Fragment>
);
}

View file

@ -6,23 +6,23 @@ import DevicesIcon from '../icons/Devices';
import commonStyles from '../common.styles.css';
export default function DevicesOverview(props) {
return (
<div className={cx(commonStyles.sidebarContentSection)}>
<div className={cx(commonStyles.sidebarContentSectionTitleBar)}>
<DevicesIcon color="white" width={26} margin={2} /> Devices
</div>
<div className={cx(commonStyles.sidebarContentSectionContainer)}>
<div
className={cx(
commonStyles.flexContainer,
commonStyles.flexContainerSpaceBetween
)}
>
{props.browser.devices.length} active device
{props.browser.devices.length !== 1 && 's'}
<DeviceManagerContainer />
return (
<div className={cx(commonStyles.sidebarContentSection)}>
<div className={cx(commonStyles.sidebarContentSectionTitleBar)}>
<DevicesIcon color="white" width={26} margin={2} /> Devices
</div>
<div className={cx(commonStyles.sidebarContentSectionContainer)}>
<div
className={cx(
commonStyles.flexContainer,
commonStyles.flexContainerSpaceBetween
)}
>
{props.browser.devices.length} active device
{props.browser.devices.length !== 1 && 's'}
<DeviceManagerContainer />
</div>
</div>
</div>
</div>
</div>
);
);
}

View file

@ -6,70 +6,78 @@ import Renderer from '../Renderer';
import styles from './style.module.css';
import {
HORIZONTAL_LAYOUT,
FLEXIGRID_LAYOUT,
INDIVIDUAL_LAYOUT,
HORIZONTAL_LAYOUT,
FLEXIGRID_LAYOUT,
INDIVIDUAL_LAYOUT,
} from '../../constants/previewerLayouts';
import {isDeviceEligible} from '../../utils/filterUtils';
import {getDeviceIcon} from '../../utils/iconUtils';
export default function DevicesPreviewer(props) {
const {
browser: {
devices,
address,
zoomLevel,
previewer: {layout},
},
} = props;
const [activeTab, changeTab] = useState(0);
const {
browser: {
devices,
address,
zoomLevel,
previewer: {layout},
},
} = props;
const [activeTab, changeTab] = useState(0);
return (
<div className={cx(styles.container)}>
{layout === INDIVIDUAL_LAYOUT && (
<Tabs onSelect={changeTab}>
<TabList>
{devices
.map(device => {
if (!isDeviceEligible(device, props.browser.filters)) {
return null;
}
return (
<Tab tabId={device.id}>
{getDeviceIcon(device.type)}
{device.name}
</Tab>
);
})
.filter(Boolean)}
</TabList>
</Tabs>
)}
<div
className={cx(styles.devicesContainer, {
[styles.flexigrid]: layout === FLEXIGRID_LAYOUT,
[styles.horizontal]: layout === HORIZONTAL_LAYOUT,
})}
>
{devices.map((device, index) => (
<div
key={device.id}
className={cx({
[styles.tab]: layout === INDIVIDUAL_LAYOUT,
[styles.activeTab]:
layout === INDIVIDUAL_LAYOUT && activeTab === index,
})}
>
<Renderer
hidden={!isDeviceEligible(device, props.browser.filters)}
device={device}
src={address}
zoomLevel={zoomLevel}
transmitNavigatorStatus={index === 0}
/>
</div>
))}
</div>
</div>
);
return (
<div className={cx(styles.container)}>
{layout === INDIVIDUAL_LAYOUT && (
<Tabs onSelect={changeTab}>
<TabList>
{devices
.map(device => {
if (
!isDeviceEligible(
device,
props.browser.filters
)
) {
return null;
}
return (
<Tab tabId={device.id}>
{getDeviceIcon(device.type)}
{device.name}
</Tab>
);
})
.filter(Boolean)}
</TabList>
</Tabs>
)}
<div
className={cx(styles.devicesContainer, {
[styles.flexigrid]: layout === FLEXIGRID_LAYOUT,
[styles.horizontal]: layout === HORIZONTAL_LAYOUT,
})}
>
{devices.map((device, index) => (
<div
key={device.id}
className={cx({
[styles.tab]: layout === INDIVIDUAL_LAYOUT,
[styles.activeTab]:
layout === INDIVIDUAL_LAYOUT &&
activeTab === index,
})}
>
<Renderer
hidden={
!isDeviceEligible(device, props.browser.filters)
}
device={device}
src={address}
zoomLevel={zoomLevel}
transmitNavigatorStatus={index === 0}
/>
</div>
))}
</div>
</div>
);
}

View file

@ -7,42 +7,42 @@ import DevicesPreviewer from './';
const testSrc = 'https://testUrl.com';
const testDevice1 = {
id: 1,
name: 'testDevice1',
width: 100,
height: 100,
id: 1,
name: 'testDevice1',
width: 100,
height: 100,
};
const testDevice2 = {
id: 2,
name: 'testDevice2',
width: 200,
height: 200,
id: 2,
name: 'testDevice2',
width: 200,
height: 200,
};
const testDevices = [testDevice1, testDevice2];
describe('<DevicesPreviewer />', () => {
it('Renders the Renderer for all passed array of devices', () => {
const wrapper = shallow(
<DevicesPreviewer devices={testDevices} url={testSrc} />
);
expect(wrapper.find('Renderer')).to.have.lengthOf(testDevices.length);
});
it('Renders the Renderer for all devices passed with the given url', () => {
const wrapper = shallow(
<DevicesPreviewer devices={testDevices} url={testSrc} />
);
wrapper.find('Renderer').forEach(renderer => {
expect(renderer.prop('src')).to.equal(testSrc);
it('Renders the Renderer for all passed array of devices', () => {
const wrapper = shallow(
<DevicesPreviewer devices={testDevices} url={testSrc} />
);
expect(wrapper.find('Renderer')).to.have.lengthOf(testDevices.length);
});
});
it('Renders the Renderer for all devices in the given order', () => {
const wrapper = shallow(
<DevicesPreviewer devices={testDevices} url={testSrc} />
);
wrapper.find('Renderer').forEach((renderer, idx) => {
expect(renderer.prop('device')).to.equal(testDevices[idx]);
it('Renders the Renderer for all devices passed with the given url', () => {
const wrapper = shallow(
<DevicesPreviewer devices={testDevices} url={testSrc} />
);
wrapper.find('Renderer').forEach(renderer => {
expect(renderer.prop('src')).to.equal(testSrc);
});
});
it('Renders the Renderer for all devices in the given order', () => {
const wrapper = shallow(
<DevicesPreviewer devices={testDevices} url={testSrc} />
);
wrapper.find('Renderer').forEach((renderer, idx) => {
expect(renderer.prop('device')).to.equal(testDevices[idx]);
});
});
});
});

View file

@ -16,58 +16,58 @@ import {iconsColor} from '../../constants/colors';
import DoubleLeftArrowIcon from '../icons/DoubleLeftArrow';
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
justifyContent: 'flex-end',
margin: '0 0 10px',
'&:hover': {},
},
iconHover: {
color: iconsColor,
opacity: 0.8,
borderRadius: '50%',
'&:hover': {
opacity: 1,
color: '#8c8c8c42',
root: {
display: 'flex',
justifyContent: 'flex-end',
margin: '0 0 10px',
'&:hover': {},
},
iconHover: {
color: iconsColor,
opacity: 0.8,
borderRadius: '50%',
'&:hover': {
opacity: 1,
color: '#8c8c8c42',
},
},
},
}));
export function Drawer(props) {
const classes = useStyles();
return (
<div className={cx(styles.drawer, {[styles.open]: props.drawer.open})}>
<div
className={classes.root}
onClick={() => props.changeDrawerOpenState(false)}
>
<div
className={cx(
commonStyles.flexContainer,
commonStyles.icons,
commonStyles.enabled
)}
onClick={() => props.changeDrawerOpenState(false)}
>
<DoubleLeftArrowIcon color="white" height={30} />
const classes = useStyles();
return (
<div className={cx(styles.drawer, {[styles.open]: props.drawer.open})}>
<div
className={classes.root}
onClick={() => props.changeDrawerOpenState(false)}
>
<div
className={cx(
commonStyles.flexContainer,
commonStyles.icons,
commonStyles.enabled
)}
onClick={() => props.changeDrawerOpenState(false)}
>
<DoubleLeftArrowIcon color="white" height={30} />
</div>
{/*<Icon type="chevronsLeft" title="Close Drawer" color="white" className={classes.iconHover} />*/}
{/*<ChevronLeftIcon className={classes.iconHover} />*/}
</div>
<div className={styles.contentContainer}>
{getDrawerContent(props.drawer.content)}
</div>
</div>
{/*<Icon type="chevronsLeft" title="Close Drawer" color="white" className={classes.iconHover} />*/}
{/*<ChevronLeftIcon className={classes.iconHover} />*/}
</div>
<div className={styles.contentContainer}>
{getDrawerContent(props.drawer.content)}
</div>
</div>
);
);
}
const deviceDrawerComponent = <DeviceDrawerContainer />;
function getDrawerContent(type) {
switch (type) {
case DEVICE_MANAGER:
return deviceDrawerComponent;
}
switch (type) {
case DEVICE_MANAGER:
return deviceDrawerComponent;
}
}
export default Drawer;

View file

@ -1,32 +1,32 @@
import React, {Fragment} from 'react';
export default class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {hasError: false};
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return {hasError: true, error};
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
alert(JSON.stringify({error, erroInfo}));
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
<Fragment>
<h1>Something went wrong.</h1>
<div>JSON.stringify(error)</div>
</Fragment>
);
constructor(props) {
super(props);
this.state = {hasError: false};
}
return this.props.children;
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return {hasError: true, error};
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
alert(JSON.stringify({error, erroInfo}));
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
<Fragment>
<h1>Something went wrong.</h1>
<div>JSON.stringify(error)</div>
</Fragment>
);
}
return this.props.children;
}
}

View file

@ -12,34 +12,39 @@ import styles from './style.module.css';
import NavigationControlsContainer from '../../containers/NavigationControlsContainer';
const Header = function() {
return (
<div className={styles.header}>
<Grid container direction="row" justify="flex-start" alignItems="center">
<Grid item>
<NavigationControlsContainer />
</Grid>
<Grid item style={{flex: 1}}>
<AddressBar />
</Grid>
<Grid item>
<ScrollControlsContainer />
</Grid>
</Grid>
<HttpAuthDialog />
<ToastContainer
position="top-right"
autoClose={3000}
hideProgressBar
newestOnTop={false}
closeOnClick
pauseOnFocusLoss={false}
rtl={false}
draggable
pauseOnHover
toastClassName={styles.darkToast}
/>
</div>
);
return (
<div className={styles.header}>
<Grid
container
direction="row"
justify="flex-start"
alignItems="center"
>
<Grid item>
<NavigationControlsContainer />
</Grid>
<Grid item style={{flex: 1}}>
<AddressBar />
</Grid>
<Grid item>
<ScrollControlsContainer />
</Grid>
</Grid>
<HttpAuthDialog />
<ToastContainer
position="top-right"
autoClose={3000}
hideProgressBar
newestOnTop={false}
closeOnClick
pauseOnFocusLoss={false}
rtl={false}
draggable
pauseOnHover
toastClassName={styles.darkToast}
/>
</div>
);
};
export default Header;

View file

@ -4,11 +4,10 @@ import {expect} from 'chai';
import Header from './';
describe('<Header />', () => {
it('renders a h1 and BrowserZoom components', () => {
const wrapper = shallow(<Header />);
expect(wrapper.find('h1')).to.have.lengthOf(1);
expect(wrapper.find('ZoomInput')).to.have.lengthOf(1);
})
});
it('renders a h1 and BrowserZoom components', () => {
const wrapper = shallow(<Header />);
expect(wrapper.find('h1')).to.have.lengthOf(1);
expect(wrapper.find('ZoomInput')).to.have.lengthOf(1);
});
});

View file

@ -8,14 +8,14 @@ import styles from './Home.css';
type Props = {};
export default class Home extends Component<Props> {
props: Props;
props: Props;
render() {
return (
<div className={styles.container} data-tid="container">
<AddressBar address="https://www.google.com" />
<h2>Home</h2>
</div>
);
}
render() {
return (
<div className={styles.container} data-tid="container">
<AddressBar address="https://www.google.com" />
<h2>Home</h2>
</div>
);
}
}

View file

@ -9,85 +9,89 @@ import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
export default function HttpAuthDialog() {
const [open, setOpen] = useState(false);
const [url, setUrl] = useState('');
const usernameRef = useRef(null);
const passwordRef = useRef(null);
const [open, setOpen] = useState(false);
const [url, setUrl] = useState('');
const usernameRef = useRef(null);
const passwordRef = useRef(null);
ipcRenderer.on('http-auth-prompt', (event, args) => {
console.log('HTTP msg', event, args);
setUrl(args.url);
setOpen(true);
});
function handleClose(status) {
if (!status) {
ipcRenderer.send('http-auth-promt-response', {url});
}
ipcRenderer.send('http-auth-promt-response', {
url,
username: usernameRef.current.querySelector('input').value,
password: passwordRef.current.querySelector('input').value,
ipcRenderer.on('http-auth-prompt', (event, args) => {
console.log('HTTP msg', event, args);
setUrl(args.url);
setOpen(true);
});
setOpen(false);
}
return (
<div>
<Dialog
open={open}
onClose={handleClose}
disableBackdropClick={true}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Sign-in</DialogTitle>
<form
id="my-form-id"
onSubmit={e => {
e.preventDefault();
handleClose(true);
}}
>
<DialogContent>
<DialogContentText>
{url ? <strong>{url}</strong> : 'The webpage'} requires HTTP Basic
authentication to connect, please enter the details to continue.
</DialogContentText>
function handleClose(status) {
if (!status) {
ipcRenderer.send('http-auth-promt-response', {url});
}
ipcRenderer.send('http-auth-promt-response', {
url,
username: usernameRef.current.querySelector('input').value,
password: passwordRef.current.querySelector('input').value,
});
setOpen(false);
}
<TextField
ref={usernameRef}
autoFocus
margin="dense"
id="username"
label="Username"
type="text"
fullWidth
/>
<TextField
ref={passwordRef}
margin="dense"
id="password"
label="Password"
type="password"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={() => handleClose(false)} color="secondary">
Cancel
</Button>
<Button
variant="contained"
onClick={() => handleClose(true)}
color="primary"
type="submit"
primary={true}
return (
<div>
<Dialog
open={open}
onClose={handleClose}
disableBackdropClick={true}
aria-labelledby="form-dialog-title"
>
Sign In
</Button>
</DialogActions>
</form>
</Dialog>
</div>
);
<DialogTitle id="form-dialog-title">Sign-in</DialogTitle>
<form
id="my-form-id"
onSubmit={e => {
e.preventDefault();
handleClose(true);
}}
>
<DialogContent>
<DialogContentText>
{url ? <strong>{url}</strong> : 'The webpage'}{' '}
requires HTTP Basic authentication to connect,
please enter the details to continue.
</DialogContentText>
<TextField
ref={usernameRef}
autoFocus
margin="dense"
id="username"
label="Username"
type="text"
fullWidth
/>
<TextField
ref={passwordRef}
margin="dense"
id="password"
label="Password"
type="password"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button
onClick={() => handleClose(false)}
color="secondary"
>
Cancel
</Button>
<Button
variant="contained"
onClick={() => handleClose(true)}
color="primary"
type="submit"
primary={true}
>
Sign In
</Button>
</DialogActions>
</form>
</Dialog>
</div>
);
}

View file

@ -11,56 +11,63 @@ import styles from './styles.css';
import commonStyles from '../common.styles.css';
import {iconsColor} from '../../constants/colors';
import {
DEVICE_MANAGER,
SCREENSHOT_MANAGER,
DEVICE_MANAGER,
SCREENSHOT_MANAGER,
} from '../../constants/DrawerContents';
const LeftIconsPane = props => {
const headwayRef = useRef();
const iconProps = {
color: iconsColor,
style: {fontSize: 30},
height: 30,
width: 30,
};
return (
<div className={styles.iconsContainer}>
<div className={cx(styles.icon, styles.logo)}>
<Logo width={40} height={40} />
</div>
<Grid
container
spacing={1}
direction="column"
alignItems="center"
className={cx(styles.utilitySection)}
>
<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<div onClick={() => props.openDrawerAndSetContent(DEVICE_MANAGER)}>
<DevicesIcon {...iconProps} />
</div>
</Grid>
{/*<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
const headwayRef = useRef();
const iconProps = {
color: iconsColor,
style: {fontSize: 30},
height: 30,
width: 30,
};
return (
<div className={styles.iconsContainer}>
<div className={cx(styles.icon, styles.logo)}>
<Logo width={40} height={40} />
</div>
<Grid
container
spacing={1}
direction="column"
alignItems="center"
className={cx(styles.utilitySection)}
>
<Grid
item
className={cx(commonStyles.icons, commonStyles.enabled)}
>
<div
onClick={() =>
props.openDrawerAndSetContent(DEVICE_MANAGER)
}
>
<DevicesIcon {...iconProps} />
</div>
</Grid>
{/*<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<div
onClick={() => props.openDrawerAndSetContent(SCREENSHOT_MANAGER)}
>
<PhotoLibraryIcon {...iconProps} />
</div>
</Grid>*/}
</Grid>
<div style={{position: 'relative'}}>
<div
id="headway"
ref={headwayRef}
className={cx(
styles.updates,
commonStyles.icons,
commonStyles.enabled
)}
/>
</div>
</div>
);
</Grid>
<div style={{position: 'relative'}}>
<div
id="headway"
ref={headwayRef}
className={cx(
styles.updates,
commonStyles.icons,
commonStyles.enabled
)}
/>
</div>
</div>
);
};
export default LeftIconsPane;

View file

@ -14,79 +14,93 @@ import {iconsColor} from '../../constants/colors';
import {Tooltip} from '@material-ui/core';
class NavigationControls extends Component {
render() {
const iconProps = {
color: iconsColor,
height: 25,
width: 25,
};
const {backEnabled, forwardEnabled} = this.props;
return (
<div className={styles.navigationControls}>
<Grid container spacing={1} alignItems="center">
<Grid
item
className={cx(commonStyles.icons, {
[commonStyles.disabled]: !backEnabled,
[commonStyles.enabled]: backEnabled,
})}
>
<div
className={cx(commonStyles.iconDisabler, {
[commonStyles.disabled]: !backEnabled,
})}
/>
<Tooltip title="Back">
<div onClick={this.props.triggerNavigationBack}>
<Icon type="arrowLeft" size="30px" {...iconProps} />
{/*<ArrowLeftIcon {...iconProps} />*/}
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonStyles.icons, {
[commonStyles.disabled]: !forwardEnabled,
[commonStyles.enabled]: forwardEnabled,
})}
>
<div
className={cx(commonStyles.iconDisabler, {
[commonStyles.disabled]: !forwardEnabled,
})}
/>
<Tooltip title="Forward">
<div onClick={this.props.triggerNavigationForward}>
<Icon type="arrowRight" size="30px" {...iconProps} />
{/*<ArrowRightIcon {...iconProps} />*/}
</div>
</Tooltip>
</Grid>
<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<Tooltip title="Reload">
<div
onClick={this.props.triggerNavigationReload}
style={{transform: 'rotate(90deg)'}}
>
<Icon type="rotate" {...iconProps} />
{/*<ReloadIcon {...iconProps} height={15} width={15} padding={5} />*/}
</div>
</Tooltip>
</Grid>
<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<Tooltip title="Go to Homepage">
<div
className={commonStyles.flexAlignVerticalMiddle}
onClick={this.props.goToHomepage}
>
<HomeIcon {...iconProps} padding={5} />
</div>
</Tooltip>
</Grid>
</Grid>
</div>
);
}
render() {
const iconProps = {
color: iconsColor,
height: 25,
width: 25,
};
const {backEnabled, forwardEnabled} = this.props;
return (
<div className={styles.navigationControls}>
<Grid container spacing={1} alignItems="center">
<Grid
item
className={cx(commonStyles.icons, {
[commonStyles.disabled]: !backEnabled,
[commonStyles.enabled]: backEnabled,
})}
>
<div
className={cx(commonStyles.iconDisabler, {
[commonStyles.disabled]: !backEnabled,
})}
/>
<Tooltip title="Back">
<div onClick={this.props.triggerNavigationBack}>
<Icon
type="arrowLeft"
size="30px"
{...iconProps}
/>
{/*<ArrowLeftIcon {...iconProps} />*/}
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonStyles.icons, {
[commonStyles.disabled]: !forwardEnabled,
[commonStyles.enabled]: forwardEnabled,
})}
>
<div
className={cx(commonStyles.iconDisabler, {
[commonStyles.disabled]: !forwardEnabled,
})}
/>
<Tooltip title="Forward">
<div onClick={this.props.triggerNavigationForward}>
<Icon
type="arrowRight"
size="30px"
{...iconProps}
/>
{/*<ArrowRightIcon {...iconProps} />*/}
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonStyles.icons, commonStyles.enabled)}
>
<Tooltip title="Reload">
<div
onClick={this.props.triggerNavigationReload}
style={{transform: 'rotate(90deg)'}}
>
<Icon type="rotate" {...iconProps} />
{/*<ReloadIcon {...iconProps} height={15} width={15} padding={5} />*/}
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonStyles.icons, commonStyles.enabled)}
>
<Tooltip title="Go to Homepage">
<div
className={commonStyles.flexAlignVerticalMiddle}
onClick={this.props.goToHomepage}
>
<HomeIcon {...iconProps} padding={5} />
</div>
</Tooltip>
</Grid>
</Grid>
</div>
);
}
}
export default NavigationControls;

View file

@ -8,20 +8,20 @@ import BrowserZoom from './';
import Slider from '@material-ui/core/Slider';
describe('<BrowserZoom />', () => {
it('Renders label and the slider component ', () => {
const wrapper = shallow(<BrowserZoom />);
expect(wrapper.find(Slider)).to.have.lengthOf(1);
});
it('Renders label and the slider component ', () => {
const wrapper = shallow(<BrowserZoom />);
expect(wrapper.find(Slider)).to.have.lengthOf(1);
});
it('Calls the callback on slider change', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');
wrapper.find('.MuiSlider-thumb').simulate('mouseup');
expect(onChange).to.have.property('callCount', 1);
});
it('Calls the callback on slider change', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');
wrapper.find('.MuiSlider-thumb').simulate('mouseup');
expect(onChange).to.have.property('callCount', 1);
});
/*it('Calls the callback with a number value', () => {
/*it('Calls the callback with a number value', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');

View file

@ -3,19 +3,19 @@ import Spinner from '../Spinner';
import Tick from '../icons/TickAnimation';
export default function NotificationMessage(props) {
return (
<div style={{display: 'flex'}}>
{props.spinner && (
<div style={{marginRight: 5}}>
<Spinner />
return (
<div style={{display: 'flex'}}>
{props.spinner && (
<div style={{marginRight: 5}}>
<Spinner />
</div>
)}
{props.tick && (
<div style={{marginRight: 5}}>
<Tick />
</div>
)}
{props.message}
</div>
)}
{props.tick && (
<div style={{marginRight: 5}}>
<Tick />
</div>
)}
{props.message}
</div>
);
);
}

View file

@ -3,61 +3,61 @@ import cx from 'classnames';
import Select from 'react-select';
import LayoutIcon from '../icons/Layout';
import {
HORIZONTAL_LAYOUT,
FLEXIGRID_LAYOUT,
INDIVIDUAL_LAYOUT,
HORIZONTAL_LAYOUT,
FLEXIGRID_LAYOUT,
INDIVIDUAL_LAYOUT,
} from '../../constants/previewerLayouts';
import commonStyles from '../common.styles.css';
const options = [
{value: HORIZONTAL_LAYOUT, label: 'Horizontal'},
{value: FLEXIGRID_LAYOUT, label: 'FlexiGrid'},
{value: INDIVIDUAL_LAYOUT, label: 'Individual'},
{value: HORIZONTAL_LAYOUT, label: 'Horizontal'},
{value: FLEXIGRID_LAYOUT, label: 'FlexiGrid'},
{value: INDIVIDUAL_LAYOUT, label: 'Individual'},
];
const styles = {
control: styles => ({...styles, backgroundColor: '#ffffff10'}),
option: (styles, {data, isDisabled, isFocused, isSelected}) => {
const color = 'white';
return {
...styles,
backgroundColor: isDisabled
? null
: isSelected
? '#ffffff40'
: isFocused
? '#ffffff20'
: null,
color: 'white',
control: styles => ({...styles, backgroundColor: '#ffffff10'}),
option: (styles, {data, isDisabled, isFocused, isSelected}) => {
const color = 'white';
return {
...styles,
backgroundColor: isDisabled
? null
: isSelected
? '#ffffff40'
: isFocused
? '#ffffff20'
: null,
color: 'white',
':active': {
...styles[':active'],
backgroundColor: !isDisabled && '#ffffff40',
},
};
},
input: styles => ({...styles}),
placeholder: styles => ({...styles}),
singleValue: (styles, {data}) => ({...styles, color: 'white'}),
menu: styles => ({...styles, background: '#4b4b4b', zIndex: 100}),
':active': {
...styles[':active'],
backgroundColor: !isDisabled && '#ffffff40',
},
};
},
input: styles => ({...styles}),
placeholder: styles => ({...styles}),
singleValue: (styles, {data}) => ({...styles, color: 'white'}),
menu: styles => ({...styles, background: '#4b4b4b', zIndex: 100}),
};
export default function PreviewerLayoutSelector(props) {
return (
<div className={cx(commonStyles.sidebarContentSection)}>
<div className={cx(commonStyles.sidebarContentSectionTitleBar)}>
<LayoutIcon height={18} margin={6} color="white" />
Layout
</div>
<div className={cx(commonStyles.sidebarContentSectionContainer)}>
<Select
options={options}
value={options.find(option => option.value === props.value)}
onChange={props.onChange}
styles={styles}
/>
</div>
</div>
);
return (
<div className={cx(commonStyles.sidebarContentSection)}>
<div className={cx(commonStyles.sidebarContentSectionTitleBar)}>
<LayoutIcon height={18} margin={6} color="white" />
Layout
</div>
<div className={cx(commonStyles.sidebarContentSectionContainer)}>
<Select
options={options}
value={options.find(option => option.value === props.value)}
onChange={props.onChange}
styles={styles}
/>
</div>
</div>
);
}

View file

@ -15,125 +15,189 @@ import {OS, DEVICE_TYPE} from '../../constants/devices';
import {FILTER_FIELDS} from '../../reducers/browser';
export default function QuickFilterDevices(props) {
return (
<div className={cx(commonStyles.sidebarContentSection)}>
<div className={cx(commonStyles.sidebarContentSectionTitleBar)}>
<Icon type="filter" color="white" size="30px" /> Quick Filters
</div>
<div className={cx(commonStyles.sidebarContentSectionContainer)}>
<div className={cx(styles.filterSection)}>
<div className={cx(styles.sectionTitle)}>Operating System</div>
<div className={cx(styles.optionIconsContainer)}>
<div
className={cx(styles.optionIcon, commonStyles.icons, {
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[FILTER_FIELDS.OS].indexOf(OS.ios) !==
-1,
})}
onClick={() => props.toggleFilter(FILTER_FIELDS.OS, OS.ios)}
>
<AppleIcon color={iconsColor} height={40} />
return (
<div className={cx(commonStyles.sidebarContentSection)}>
<div className={cx(commonStyles.sidebarContentSectionTitleBar)}>
<Icon type="filter" color="white" size="30px" /> Quick Filters
</div>
<div
className={cx(styles.optionIcon, commonStyles.icons, {
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[FILTER_FIELDS.OS].indexOf(
OS.android
) !== -1,
})}
onClick={() => props.toggleFilter(FILTER_FIELDS.OS, OS.android)}
>
<AndroidIcon color={iconsColor} style={{fontSize: 40}} />
<div className={cx(commonStyles.sidebarContentSectionContainer)}>
<div className={cx(styles.filterSection)}>
<div className={cx(styles.sectionTitle)}>
Operating System
</div>
<div className={cx(styles.optionIconsContainer)}>
<div
className={cx(
styles.optionIcon,
commonStyles.icons,
{
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[
FILTER_FIELDS.OS
].indexOf(OS.ios) !== -1,
}
)}
onClick={() =>
props.toggleFilter(FILTER_FIELDS.OS, OS.ios)
}
>
<AppleIcon color={iconsColor} height={40} />
</div>
<div
className={cx(
styles.optionIcon,
commonStyles.icons,
{
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[
FILTER_FIELDS.OS
].indexOf(OS.android) !== -1,
}
)}
onClick={() =>
props.toggleFilter(FILTER_FIELDS.OS, OS.android)
}
>
<AndroidIcon
color={iconsColor}
style={{fontSize: 40}}
/>
</div>
<div
className={cx(
styles.optionIcon,
commonStyles.icons,
{
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[
FILTER_FIELDS.OS
].indexOf(OS.windowsPhone) !== -1,
}
)}
onClick={() =>
props.toggleFilter(
FILTER_FIELDS.OS,
OS.windowsPhone
)
}
>
<WindowsIcon
color={iconsColor}
height={34}
padding={3}
/>
</div>
<div
className={cx(
styles.optionIcon,
commonStyles.icons,
{
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[
FILTER_FIELDS.OS
].indexOf(OS.pc) !== -1,
}
)}
onClick={() =>
props.toggleFilter(FILTER_FIELDS.OS, OS.pc)
}
>
<DesktopIcon
color={iconsColor}
style={{fontSize: 40}}
/>
</div>
</div>
</div>
<div className={cx(styles.filterSection)}>
<div className={cx(styles.sectionTitle)}>Device</div>
<div className={cx(styles.optionIconsContainer)}>
<div
className={cx(
styles.optionIcon,
commonStyles.icons,
{
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[
FILTER_FIELDS.DEVICE_TYPE
].indexOf(DEVICE_TYPE.phone) !== -1,
}
)}
onClick={() =>
props.toggleFilter(
FILTER_FIELDS.DEVICE_TYPE,
DEVICE_TYPE.phone
)
}
>
<MobileIcon
color={iconsColor}
style={{fontSize: 35}}
/>
</div>
<div
className={cx(
styles.optionIcon,
commonStyles.icons,
{
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[
FILTER_FIELDS.DEVICE_TYPE
].indexOf(DEVICE_TYPE.tablet) !== -1,
}
)}
onClick={() =>
props.toggleFilter(
FILTER_FIELDS.DEVICE_TYPE,
DEVICE_TYPE.tablet
)
}
>
<TabletIcon
color={iconsColor}
style={{fontSize: 35}}
/>
</div>
<div
className={cx(
styles.optionIcon,
commonStyles.icons,
{
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[
FILTER_FIELDS.DEVICE_TYPE
].indexOf(DEVICE_TYPE.desktop) !== -1,
}
)}
onClick={() =>
props.toggleFilter(
FILTER_FIELDS.DEVICE_TYPE,
DEVICE_TYPE.desktop
)
}
>
<DesktopIcon
color={iconsColor}
style={{fontSize: 40}}
/>
</div>
</div>
</div>
</div>
<div
className={cx(styles.optionIcon, commonStyles.icons, {
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[FILTER_FIELDS.OS].indexOf(
OS.windowsPhone
) !== -1,
})}
onClick={() =>
props.toggleFilter(FILTER_FIELDS.OS, OS.windowsPhone)
}
>
<WindowsIcon color={iconsColor} height={34} padding={3} />
</div>
<div
className={cx(styles.optionIcon, commonStyles.icons, {
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[FILTER_FIELDS.OS].indexOf(OS.pc) !== -1,
})}
onClick={() => props.toggleFilter(FILTER_FIELDS.OS, OS.pc)}
>
<DesktopIcon color={iconsColor} style={{fontSize: 40}} />
</div>
</div>
</div>
<div className={cx(styles.filterSection)}>
<div className={cx(styles.sectionTitle)}>Device</div>
<div className={cx(styles.optionIconsContainer)}>
<div
className={cx(styles.optionIcon, commonStyles.icons, {
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[FILTER_FIELDS.DEVICE_TYPE].indexOf(
DEVICE_TYPE.phone
) !== -1,
})}
onClick={() =>
props.toggleFilter(FILTER_FIELDS.DEVICE_TYPE, DEVICE_TYPE.phone)
}
>
<MobileIcon color={iconsColor} style={{fontSize: 35}} />
</div>
<div
className={cx(styles.optionIcon, commonStyles.icons, {
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[FILTER_FIELDS.DEVICE_TYPE].indexOf(
DEVICE_TYPE.tablet
) !== -1,
})}
onClick={() =>
props.toggleFilter(
FILTER_FIELDS.DEVICE_TYPE,
DEVICE_TYPE.tablet
)
}
>
<TabletIcon color={iconsColor} style={{fontSize: 35}} />
</div>
<div
className={cx(styles.optionIcon, commonStyles.icons, {
[commonStyles.disabled]: false,
[commonStyles.enabled]: true,
[commonStyles.selected]:
props.browser.filters[FILTER_FIELDS.DEVICE_TYPE].indexOf(
DEVICE_TYPE.desktop
) !== -1,
})}
onClick={() =>
props.toggleFilter(
FILTER_FIELDS.DEVICE_TYPE,
DEVICE_TYPE.desktop
)
}
>
<DesktopIcon color={iconsColor} style={{fontSize: 40}} />
</div>
</div>
</div>
</div>
</div>
);
);
}

View file

@ -9,27 +9,29 @@ import styles from './style.module.css';
import {getDeviceIcon} from '../../utils/iconUtils';
function Renderer(props) {
const [loading, setLoading] = useState(true);
return (
<div className={cx(styles.container, {[styles.hidden]: props.hidden})}>
<div className={styles.titleContainer}>
{getDeviceIcon(props.device.type)}
<span className={cx(styles.deviceTitle)}>{props.device.name}</span>
{loading && (
<div>
<Spinner size={16} />
</div>
)}
</div>
<div className={cx(styles.deviceWrapper)}>
<WebViewContainer
device={props.device}
transmitNavigatorStatus={props.transmitNavigatorStatus}
onLoadingStateChange={setLoading}
/>
</div>
</div>
);
const [loading, setLoading] = useState(true);
return (
<div className={cx(styles.container, {[styles.hidden]: props.hidden})}>
<div className={styles.titleContainer}>
{getDeviceIcon(props.device.type)}
<span className={cx(styles.deviceTitle)}>
{props.device.name}
</span>
{loading && (
<div>
<Spinner size={16} />
</div>
)}
</div>
<div className={cx(styles.deviceWrapper)}>
<WebViewContainer
device={props.device}
transmitNavigatorStatus={props.transmitNavigatorStatus}
onLoadingStateChange={setLoading}
/>
</div>
</div>
);
}
export default Renderer;

View file

@ -7,35 +7,47 @@ import Renderer from './';
const testSrc = 'https://testUrl.com';
const testDevice1 = {
name: 'testDevice1',
width: 100,
height: 100,
name: 'testDevice1',
width: 100,
height: 100,
};
describe('<Renderer />', () => {
it('Renders the header and the iframe', () => {
const wrapper = shallow(<Renderer src={testSrc} device={testDevice1} />);
expect(wrapper.find('iframe')).to.have.lengthOf(1);
expect(wrapper.find('h2')).to.have.lengthOf(1);
});
it('Renders the header and the iframe', () => {
const wrapper = shallow(
<Renderer src={testSrc} device={testDevice1} />
);
expect(wrapper.find('iframe')).to.have.lengthOf(1);
expect(wrapper.find('h2')).to.have.lengthOf(1);
});
it('Renders the header with the device name', () => {
const wrapper = shallow(<Renderer src={testSrc} device={testDevice1} />);
expect(wrapper.find('h2').text()).to.equal(testDevice1.name);
});
it('Renders the header with the device name', () => {
const wrapper = shallow(
<Renderer src={testSrc} device={testDevice1} />
);
expect(wrapper.find('h2').text()).to.equal(testDevice1.name);
});
it('Renders the iframe with the given device dimensions', () => {
const wrapper = shallow(<Renderer src={testSrc} device={testDevice1} />);
expect(wrapper.find('iframe').prop('width')).to.equal(testDevice1.width);
expect(wrapper.find('iframe').prop('height')).to.equal(testDevice1.height);
});
it('Renders the iframe with the given device dimensions', () => {
const wrapper = shallow(
<Renderer src={testSrc} device={testDevice1} />
);
expect(wrapper.find('iframe').prop('width')).to.equal(
testDevice1.width
);
expect(wrapper.find('iframe').prop('height')).to.equal(
testDevice1.height
);
});
it('Renders the iframe with the given url', () => {
const wrapper = shallow(<Renderer src={testSrc} device={testDevice1} />);
expect(wrapper.find('iframe').prop('src')).to.equal(testSrc);
});
it('Renders the iframe with the given url', () => {
const wrapper = shallow(
<Renderer src={testSrc} device={testDevice1} />
);
expect(wrapper.find('iframe').prop('src')).to.equal(testSrc);
});
/*it('Calls the callback with a number value', () => {
/*it('Calls the callback with a number value', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');

View file

@ -1,5 +1,5 @@
import React from 'react';
export default function Screenshotmanager(props) {
return '';
return '';
}

View file

@ -15,55 +15,74 @@ import ZoomContainer from '../../containers/ZoomContainer';
import Tooltip from '@material-ui/core/Tooltip';
class ScrollControls extends Component {
render() {
const iconProps = {
color: iconsColor,
height: 25,
width: 25,
};
return (
<div className={styles.scrollControls}>
<Grid container spacing={1} alignItems="center">
<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<Tooltip title="Scroll Down">
<div onClick={this.props.triggerScrollDown}>
<ScrollDownIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<Tooltip title="Scroll Up">
<div onClick={this.props.triggerScrollUp}>
<ScrollUpIcon {...iconProps} height={30} width={30} />
</div>
</Tooltip>
</Grid>
<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<Tooltip title="Take Screenshot">
<div onClick={this.props.screenshotAllDevices}>
<ScreenshotIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<Tooltip title="Tilt Devices">
<div onClick={this.props.flipOrientationAllDevices}>
<DeviceRotateIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={cx(commonStyles.icons, commonStyles.enabled)}>
<Tooltip title="Inspect Element">
<div onClick={this.props.enableInpector}>
<InspectElementIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<ZoomContainer />
</Grid>
</div>
);
}
render() {
const iconProps = {
color: iconsColor,
height: 25,
width: 25,
};
return (
<div className={styles.scrollControls}>
<Grid container spacing={1} alignItems="center">
<Grid
item
className={cx(commonStyles.icons, commonStyles.enabled)}
>
<Tooltip title="Scroll Down">
<div onClick={this.props.triggerScrollDown}>
<ScrollDownIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonStyles.icons, commonStyles.enabled)}
>
<Tooltip title="Scroll Up">
<div onClick={this.props.triggerScrollUp}>
<ScrollUpIcon
{...iconProps}
height={30}
width={30}
/>
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonStyles.icons, commonStyles.enabled)}
>
<Tooltip title="Take Screenshot">
<div onClick={this.props.screenshotAllDevices}>
<ScreenshotIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonStyles.icons, commonStyles.enabled)}
>
<Tooltip title="Tilt Devices">
<div onClick={this.props.flipOrientationAllDevices}>
<DeviceRotateIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonStyles.icons, commonStyles.enabled)}
>
<Tooltip title="Inspect Element">
<div onClick={this.props.enableInpector}>
<InspectElementIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<ZoomContainer />
</Grid>
</div>
);
}
}
export default ScrollControls;

View file

@ -8,20 +8,20 @@ import BrowserZoom from './';
import Slider from '@material-ui/core/Slider';
describe('<BrowserZoom />', () => {
it('Renders label and the slider component ', () => {
const wrapper = shallow(<BrowserZoom />);
expect(wrapper.find(Slider)).to.have.lengthOf(1);
});
it('Renders label and the slider component ', () => {
const wrapper = shallow(<BrowserZoom />);
expect(wrapper.find(Slider)).to.have.lengthOf(1);
});
it('Calls the callback on slider change', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');
wrapper.find('.MuiSlider-thumb').simulate('mouseup');
expect(onChange).to.have.property('callCount', 1);
});
it('Calls the callback on slider change', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');
wrapper.find('.MuiSlider-thumb').simulate('mouseup');
expect(onChange).to.have.property('callCount', 1);
});
/*it('Calls the callback with a number value', () => {
/*it('Calls the callback with a number value', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');

View file

@ -3,43 +3,43 @@ import CircularProgress from '@material-ui/core/CircularProgress';
import {makeStyles} from '@material-ui/core/styles';
export default function(props) {
const classes = makeStyles({
root: {
width: '100%',
height: '100%',
position: 'relative',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
top: {
color: '#ffffff00', //'#eef3fd',
},
bottom: {
color: '#7587ec', //'#6798e5',
animationDuration: '550ms',
position: 'absolute',
},
})();
const classes = makeStyles({
root: {
width: '100%',
height: '100%',
position: 'relative',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
top: {
color: '#ffffff00', //'#eef3fd',
},
bottom: {
color: '#7587ec', //'#6798e5',
animationDuration: '550ms',
position: 'absolute',
},
})();
return (
<div className={classes.root}>
<CircularProgress
variant="determinate"
value={100}
className={classes.top}
size={20}
thickness={4}
{...props}
/>
<CircularProgress
variant="indeterminate"
disableShrink
className={classes.bottom}
size={20}
thickness={4}
{...props}
/>
</div>
);
return (
<div className={classes.root}>
<CircularProgress
variant="determinate"
value={100}
className={classes.top}
size={20}
thickness={4}
{...props}
/>
<CircularProgress
variant="indeterminate"
disableShrink
className={classes.bottom}
size={20}
thickness={4}
{...props}
/>
</div>
);
}

View file

@ -8,15 +8,15 @@ import DeviceRotateIcon from '../icons/DeviceRotate';
import cx from 'classnames';
import {iconsColor} from '../../constants/colors';
import {
SCROLL_DOWN,
SCROLL_UP,
NAVIGATION_BACK,
NAVIGATION_FORWARD,
NAVIGATION_RELOAD,
SCREENSHOT_ALL_DEVICES,
FLIP_ORIENTATION_ALL_DEVICES,
ENABLE_INSPECTOR_ALL_DEVICES,
DISABLE_INSPECTOR_ALL_DEVICES,
SCROLL_DOWN,
SCROLL_UP,
NAVIGATION_BACK,
NAVIGATION_FORWARD,
NAVIGATION_RELOAD,
SCREENSHOT_ALL_DEVICES,
FLIP_ORIENTATION_ALL_DEVICES,
ENABLE_INSPECTOR_ALL_DEVICES,
DISABLE_INSPECTOR_ALL_DEVICES,
} from '../../constants/pubsubEvents';
import {CAPABILITIES} from '../../constants/devices';
@ -29,287 +29,304 @@ import {Tooltip} from '@material-ui/core';
const BrowserWindow = remote.BrowserWindow;
const MESSAGE_TYPES = {
scroll: 'scroll',
click: 'click',
openDevToolsInspector: 'openDevToolsInspector',
disableInspector: 'disableInspector',
openConsole: 'openConsole',
tiltDevice: 'tiltDevice',
takeScreenshot: 'takeScreenshot',
toggleEventMirroring: 'toggleEventMirroring',
scroll: 'scroll',
click: 'click',
openDevToolsInspector: 'openDevToolsInspector',
disableInspector: 'disableInspector',
openConsole: 'openConsole',
tiltDevice: 'tiltDevice',
takeScreenshot: 'takeScreenshot',
toggleEventMirroring: 'toggleEventMirroring',
};
class WebView extends Component {
constructor(props) {
super(props);
this.webviewRef = createRef();
this.state = {
screenshotInProgress: false,
isTilted: false,
isUnplugged: false,
errorCode: null,
errorDesc: null,
};
this.subscriptions = [];
}
constructor(props) {
super(props);
this.webviewRef = createRef();
this.state = {
screenshotInProgress: false,
isTilted: false,
isUnplugged: false,
errorCode: null,
errorDesc: null,
};
this.subscriptions = [];
}
componentDidMount() {
//this.initDeviceEmulationParams();
this.webviewRef.current.addEventListener(
'ipc-message',
this.messageHandler
);
this.subscriptions.push(
pubsub.subscribe('scroll', this.processScrollEvent)
);
this.subscriptions.push(pubsub.subscribe('click', this.processClickEvent));
this.subscriptions.push(
pubsub.subscribe(SCROLL_DOWN, this.processScrollDownEvent)
);
this.subscriptions.push(
pubsub.subscribe(SCROLL_UP, this.processScrollUpEvent)
);
this.subscriptions.push(
pubsub.subscribe(NAVIGATION_BACK, this.processNavigationBackEvent)
);
this.subscriptions.push(
pubsub.subscribe(NAVIGATION_FORWARD, this.processNavigationForwardEvent)
);
this.subscriptions.push(
pubsub.subscribe(NAVIGATION_RELOAD, this.processNavigationReloadEvent)
);
this.subscriptions.push(
pubsub.subscribe(SCREENSHOT_ALL_DEVICES, this.processScreenshotEvent)
);
this.subscriptions.push(
pubsub.subscribe(
FLIP_ORIENTATION_ALL_DEVICES,
this.processFlipOrientationEvent
)
);
this.subscriptions.push(
pubsub.subscribe(
ENABLE_INSPECTOR_ALL_DEVICES,
this.processEnableInspectorEvent
)
);
this.subscriptions.push(
pubsub.subscribe(
DISABLE_INSPECTOR_ALL_DEVICES,
this.processDisableInspectorEvent
)
);
this.webviewRef.current.addEventListener('dom-ready', () => {
this.initEventTriggers(this.webviewRef.current);
});
this.webviewRef.current.addEventListener('did-start-loading', () => {
this.setState({errorCode: null, errorDesc: null});
this.props.onLoadingStateChange(true);
});
this.webviewRef.current.addEventListener('did-stop-loading', () => {
this.props.onLoadingStateChange(false);
});
this.webviewRef.current.addEventListener(
'did-fail-load',
({errorCode, errorDescription}) => {
if (errorCode === -3) {
//Aborted error, can be ignored
return;
}
this.setState({errorCode: errorCode, errorDesc: errorDescription});
}
);
this.webviewRef.current.addEventListener(
'login',
(event, request, authInfo, callback) => {
console.log(
'event, request, authInfo, callback',
event,
request,
authInfo,
callback
componentDidMount() {
//this.initDeviceEmulationParams();
this.webviewRef.current.addEventListener(
'ipc-message',
this.messageHandler
);
this.subscriptions.push(
pubsub.subscribe('scroll', this.processScrollEvent)
);
this.subscriptions.push(
pubsub.subscribe('click', this.processClickEvent)
);
this.subscriptions.push(
pubsub.subscribe(SCROLL_DOWN, this.processScrollDownEvent)
);
this.subscriptions.push(
pubsub.subscribe(SCROLL_UP, this.processScrollUpEvent)
);
this.subscriptions.push(
pubsub.subscribe(NAVIGATION_BACK, this.processNavigationBackEvent)
);
this.subscriptions.push(
pubsub.subscribe(
NAVIGATION_FORWARD,
this.processNavigationForwardEvent
)
);
this.subscriptions.push(
pubsub.subscribe(
NAVIGATION_RELOAD,
this.processNavigationReloadEvent
)
);
this.subscriptions.push(
pubsub.subscribe(
SCREENSHOT_ALL_DEVICES,
this.processScreenshotEvent
)
);
this.subscriptions.push(
pubsub.subscribe(
FLIP_ORIENTATION_ALL_DEVICES,
this.processFlipOrientationEvent
)
);
this.subscriptions.push(
pubsub.subscribe(
ENABLE_INSPECTOR_ALL_DEVICES,
this.processEnableInspectorEvent
)
);
this.subscriptions.push(
pubsub.subscribe(
DISABLE_INSPECTOR_ALL_DEVICES,
this.processDisableInspectorEvent
)
);
event.preventDefault();
callback('username', 'secret');
}
);
this.webviewRef.current.addEventListener('will-navigate', ({url}) => {
console.log('Navigating to ', url);
this.props.onAddressChange(url);
});
this.webviewRef.current.addEventListener('did-navigate', ({url}) => {
if (this.props.transmitNavigatorStatus) {
this.props.updateNavigatorStatus({
backEnabled: this.webviewRef.current.canGoBack(),
forwardEnabled: this.webviewRef.current.canGoForward(),
this.webviewRef.current.addEventListener('dom-ready', () => {
this.initEventTriggers(this.webviewRef.current);
});
}
});
this.webviewRef.current.addEventListener('devtools-opened', () => {
/*this.webviewRef.current
this.webviewRef.current.addEventListener('did-start-loading', () => {
this.setState({errorCode: null, errorDesc: null});
this.props.onLoadingStateChange(true);
});
this.webviewRef.current.addEventListener('did-stop-loading', () => {
this.props.onLoadingStateChange(false);
});
this.webviewRef.current.addEventListener(
'did-fail-load',
({errorCode, errorDescription}) => {
if (errorCode === -3) {
//Aborted error, can be ignored
return;
}
this.setState({
errorCode: errorCode,
errorDesc: errorDescription,
});
}
);
this.webviewRef.current.addEventListener(
'login',
(event, request, authInfo, callback) => {
console.log(
'event, request, authInfo, callback',
event,
request,
authInfo,
callback
);
event.preventDefault();
callback('username', 'secret');
}
);
this.webviewRef.current.addEventListener('will-navigate', ({url}) => {
console.log('Navigating to ', url);
this.props.onAddressChange(url);
});
this.webviewRef.current.addEventListener('did-navigate', ({url}) => {
if (this.props.transmitNavigatorStatus) {
this.props.updateNavigatorStatus({
backEnabled: this.webviewRef.current.canGoBack(),
forwardEnabled: this.webviewRef.current.canGoForward(),
});
}
});
this.webviewRef.current.addEventListener('devtools-opened', () => {
/*this.webviewRef.current
.getWebContents()
.devToolsWebContents.executeJavaScript(
'DevToolsAPI.enterInspectElementMode()'
);*/
});
}
componentWillUnmount() {
this.subscriptions.forEach(pubsub.unsubscribe);
}
initDeviceEmulationParams = () => {
try {
return;
this.webviewRef.current.getWebContents().enableDeviceEmulation({
screenPosition: this.isMobile ? 'mobile' : 'desktop',
screenSize: {
width: this.props.device.width,
height: this.props.device.height,
},
deviceScaleFactor: this.props.device.dpr,
});
} catch (err) {
console.log('err', err);
});
}
};
processNavigationBackEvent = () => {
this.webviewRef.current.goBack();
};
processNavigationForwardEvent = () => {
this.webviewRef.current.goForward();
};
processNavigationReloadEvent = () => {
this.webviewRef.current.reload();
};
processScrollEvent = message => {
if (
this.state.isUnplugged ||
message.sourceDeviceId === this.props.device.id
) {
return;
componentWillUnmount() {
this.subscriptions.forEach(pubsub.unsubscribe);
}
this.webviewRef.current.send('scrollMessage', message.position);
};
processClickEvent = message => {
if (
this.state.isUnplugged ||
message.sourceDeviceId === this.props.device.id
) {
return;
}
this.webviewRef.current.send('clickMessage', message);
};
initDeviceEmulationParams = () => {
try {
return;
this.webviewRef.current.getWebContents().enableDeviceEmulation({
screenPosition: this.isMobile ? 'mobile' : 'desktop',
screenSize: {
width: this.props.device.width,
height: this.props.device.height,
},
deviceScaleFactor: this.props.device.dpr,
});
} catch (err) {
console.log('err', err);
}
};
processScrollDownEvent = message => {
if (this.state.isUnplugged) {
return;
}
console.log('processScrollDownEvent', this.webviewRef);
this.webviewRef.current.send('scrollDownMessage');
};
processNavigationBackEvent = () => {
this.webviewRef.current.goBack();
};
processScrollUpEvent = message => {
if (this.state.isUnplugged) {
return;
}
this.webviewRef.current.send('scrollUpMessage');
};
processNavigationForwardEvent = () => {
this.webviewRef.current.goForward();
};
processScreenshotEvent = async ({now}) => {
this.setState({screenshotInProgress: true});
await captureFullPage(
this.props.browser.address,
this.props.device,
this.webviewRef.current,
now != null,
now
);
this.setState({screenshotInProgress: false});
};
processNavigationReloadEvent = () => {
this.webviewRef.current.reload();
};
processFlipOrientationEvent = () => {
if (!this.isMobile) {
return;
}
this._flipOrientation();
};
processScrollEvent = message => {
if (
this.state.isUnplugged ||
message.sourceDeviceId === this.props.device.id
) {
return;
}
this.webviewRef.current.send('scrollMessage', message.position);
};
processOpenDevToolsInspectorEvent = message => {
const {
x: webViewX,
y: webViewY,
} = this.webviewRef.current.getBoundingClientRect();
const {x: deviceX, y: deviceY} = message;
const zoomFactor = this.props.browser.zoomLevel;
this.webviewRef.current
.getWebContents()
.inspectElement(
Math.round(webViewX + deviceX * zoomFactor),
Math.round(webViewY + deviceY * zoomFactor)
);
};
processClickEvent = message => {
if (
this.state.isUnplugged ||
message.sourceDeviceId === this.props.device.id
) {
return;
}
this.webviewRef.current.send('clickMessage', message);
};
processEnableInspectorEvent = () => {
this.webviewRef.current.send('enableInspectorMessage');
};
processScrollDownEvent = message => {
if (this.state.isUnplugged) {
return;
}
console.log('processScrollDownEvent', this.webviewRef);
this.webviewRef.current.send('scrollDownMessage');
};
processDisableInspectorEvent = message => {
if (message.sourceDeviceId === this.props.device.id) {
return;
}
this.webviewRef.current.send('disableInspectorMessage');
};
processScrollUpEvent = message => {
if (this.state.isUnplugged) {
return;
}
this.webviewRef.current.send('scrollUpMessage');
};
messageHandler = ({channel: type, args: [message]}) => {
if (type !== MESSAGE_TYPES.toggleEventMirroring && this.state.isUnplugged) {
return;
}
switch (type) {
case MESSAGE_TYPES.scroll:
pubsub.publish('scroll', [message]);
return;
case MESSAGE_TYPES.click:
pubsub.publish('click', [message]);
return;
case MESSAGE_TYPES.openDevToolsInspector:
this.processOpenDevToolsInspectorEvent(message);
return;
case MESSAGE_TYPES.disableInspector:
this.transmitDisableInspectorToAllDevices(message);
return;
case MESSAGE_TYPES.openConsole:
this._toggleDevTools();
return;
case MESSAGE_TYPES.tiltDevice:
processScreenshotEvent = async ({now}) => {
this.setState({screenshotInProgress: true});
await captureFullPage(
this.props.browser.address,
this.props.device,
this.webviewRef.current,
now != null,
now
);
this.setState({screenshotInProgress: false});
};
processFlipOrientationEvent = () => {
if (!this.isMobile) {
return;
}
this._flipOrientation();
return;
case MESSAGE_TYPES.takeScreenshot:
this.processScreenshotEvent({});
return;
case MESSAGE_TYPES.toggleEventMirroring:
this._unPlug();
return;
}
};
};
transmitDisableInspectorToAllDevices = message => {
pubsub.publish(DISABLE_INSPECTOR_ALL_DEVICES, [message]);
};
processOpenDevToolsInspectorEvent = message => {
const {
x: webViewX,
y: webViewY,
} = this.webviewRef.current.getBoundingClientRect();
const {x: deviceX, y: deviceY} = message;
const zoomFactor = this.props.browser.zoomLevel;
this.webviewRef.current
.getWebContents()
.inspectElement(
Math.round(webViewX + deviceX * zoomFactor),
Math.round(webViewY + deviceY * zoomFactor)
);
};
initEventTriggers = webview => {
webview.getWebContents().executeJavaScript(`
processEnableInspectorEvent = () => {
this.webviewRef.current.send('enableInspectorMessage');
};
processDisableInspectorEvent = message => {
if (message.sourceDeviceId === this.props.device.id) {
return;
}
this.webviewRef.current.send('disableInspectorMessage');
};
messageHandler = ({channel: type, args: [message]}) => {
if (
type !== MESSAGE_TYPES.toggleEventMirroring &&
this.state.isUnplugged
) {
return;
}
switch (type) {
case MESSAGE_TYPES.scroll:
pubsub.publish('scroll', [message]);
return;
case MESSAGE_TYPES.click:
pubsub.publish('click', [message]);
return;
case MESSAGE_TYPES.openDevToolsInspector:
this.processOpenDevToolsInspectorEvent(message);
return;
case MESSAGE_TYPES.disableInspector:
this.transmitDisableInspectorToAllDevices(message);
return;
case MESSAGE_TYPES.openConsole:
this._toggleDevTools();
return;
case MESSAGE_TYPES.tiltDevice:
this._flipOrientation();
return;
case MESSAGE_TYPES.takeScreenshot:
this.processScreenshotEvent({});
return;
case MESSAGE_TYPES.toggleEventMirroring:
this._unPlug();
return;
}
};
transmitDisableInspectorToAllDevices = message => {
pubsub.publish(DISABLE_INSPECTOR_ALL_DEVICES, [message]);
};
initEventTriggers = webview => {
webview.getWebContents().executeJavaScript(`
responsivelyApp.deviceId = ${this.props.device.id};
document.body.addEventListener('mouseleave', () => {
window.responsivelyApp.mouseOn = false;
@ -369,10 +386,10 @@ class WebView extends Component {
true
);
`);
};
};
_toggleDevTools = () => {
/*const devtools = new BrowserWindow({
_toggleDevTools = () => {
/*const devtools = new BrowserWindow({
fullscreen: false,
acceptFirstMouse: true,
show: true,
@ -383,127 +400,140 @@ class WebView extends Component {
.getWebContents()
.setDevToolsWebContents(devtools.webContents);
this.webviewRef.current.getWebContents().openDevTools({mode: 'detach'});*/
this.webviewRef.current.getWebContents().toggleDevTools();
};
_flipOrientation = () => {
this.setState({isTilted: !this.state.isTilted});
};
_unPlug = () => {
this.setState({isUnplugged: !this.state.isUnplugged}, () => {
this.webviewRef.current.send(
'eventsMirroringState',
!this.state.isUnplugged
);
});
};
get isMobile() {
return this.props.device.capabilities.indexOf(CAPABILITIES.mobile) > -1;
}
render() {
const {device, browser} = this.props;
const deviceStyles = {
width:
this.isMobile && this.state.isTilted ? device.height : device.width,
height:
this.isMobile && this.state.isTilted ? device.width : device.height,
transform: `scale(${browser.zoomLevel})`,
this.webviewRef.current.getWebContents().toggleDevTools();
};
return (
<div
className={cx(styles.webViewContainer)}
style={{height: deviceStyles.height * browser.zoomLevel + 40}} //Hack, ref below TODO
>
<div className={cx(styles.webViewToolbar)}>
<Tooltip title="Open DevTools">
_flipOrientation = () => {
this.setState({isTilted: !this.state.isTilted});
};
_unPlug = () => {
this.setState({isUnplugged: !this.state.isUnplugged}, () => {
this.webviewRef.current.send(
'eventsMirroringState',
!this.state.isUnplugged
);
});
};
get isMobile() {
return this.props.device.capabilities.indexOf(CAPABILITIES.mobile) > -1;
}
render() {
const {device, browser} = this.props;
const deviceStyles = {
width:
this.isMobile && this.state.isTilted
? device.height
: device.width,
height:
this.isMobile && this.state.isTilted
? device.width
: device.height,
transform: `scale(${browser.zoomLevel})`,
};
return (
<div
className={cx(
styles.webViewToolbarIcons,
commonStyles.icons,
commonStyles.enabled
)}
onClick={this._toggleDevTools}
className={cx(styles.webViewContainer)}
style={{height: deviceStyles.height * browser.zoomLevel + 40}} //Hack, ref below TODO
>
<BugIcon width={20} color={iconsColor} />
<div className={cx(styles.webViewToolbar)}>
<Tooltip title="Open DevTools">
<div
className={cx(
styles.webViewToolbarIcons,
commonStyles.icons,
commonStyles.enabled
)}
onClick={this._toggleDevTools}
>
<BugIcon width={20} color={iconsColor} />
</div>
</Tooltip>
<Tooltip title="Take Screenshot">
<div
className={cx(
styles.webViewToolbarIcons,
commonStyles.icons,
commonStyles.enabled
)}
onClick={() => this.processScreenshotEvent({})}
>
<ScreenshotIcon height={18} color={iconsColor} />
</div>
</Tooltip>
<Tooltip title="Tilt Device">
<div
className={cx(
styles.webViewToolbarIcons,
commonStyles.icons,
{
[commonStyles.enabled]: this.isMobile,
[commonStyles.disabled]: !this.isMobile,
[commonStyles.selected]: this.state
.isTilted,
}
)}
onClick={this._flipOrientation}
>
<DeviceRotateIcon height={17} color={iconsColor} />
</div>
</Tooltip>
<Tooltip title="Disable event mirroring">
<div
className={cx(
styles.webViewToolbarIcons,
commonStyles.icons,
commonStyles.enabled,
{
[commonStyles.selected]: this.state
.isUnplugged,
}
)}
onClick={this._unPlug}
>
<UnplugIcon height={30} color={iconsColor} />
</div>
</Tooltip>
</div>
<div
className={cx(styles.deviceContainer)}
style={{
width: deviceStyles.width * browser.zoomLevel,
height: deviceStyles.height * browser.zoomLevel, //TODO why is this height not getting set?
}}
>
<div
className={cx(styles.deviceOverlay, {
[styles.overlayEnabled]: this.state
.screenshotInProgress,
})}
style={deviceStyles}
/>
<div
className={cx(styles.deviceOverlay, {
[styles.overlayEnabled]: this.state.errorCode,
})}
style={deviceStyles}
>
<p>ERROR: {this.state.errorCode}</p>
<p className={cx(styles.errorDesc)}>
{this.state.errorDesc}
</p>
</div>
<webview
ref={this.webviewRef}
preload="./preload.js"
className={cx(styles.device)}
src={browser.address || 'about:blank'}
useragent={device.useragent}
style={deviceStyles}
/>
</div>
</div>
</Tooltip>
<Tooltip title="Take Screenshot">
<div
className={cx(
styles.webViewToolbarIcons,
commonStyles.icons,
commonStyles.enabled
)}
onClick={() => this.processScreenshotEvent({})}
>
<ScreenshotIcon height={18} color={iconsColor} />
</div>
</Tooltip>
<Tooltip title="Tilt Device">
<div
className={cx(styles.webViewToolbarIcons, commonStyles.icons, {
[commonStyles.enabled]: this.isMobile,
[commonStyles.disabled]: !this.isMobile,
[commonStyles.selected]: this.state.isTilted,
})}
onClick={this._flipOrientation}
>
<DeviceRotateIcon height={17} color={iconsColor} />
</div>
</Tooltip>
<Tooltip title="Disable event mirroring">
<div
className={cx(
styles.webViewToolbarIcons,
commonStyles.icons,
commonStyles.enabled,
{
[commonStyles.selected]: this.state.isUnplugged,
}
)}
onClick={this._unPlug}
>
<UnplugIcon height={30} color={iconsColor} />
</div>
</Tooltip>
</div>
<div
className={cx(styles.deviceContainer)}
style={{
width: deviceStyles.width * browser.zoomLevel,
height: deviceStyles.height * browser.zoomLevel, //TODO why is this height not getting set?
}}
>
<div
className={cx(styles.deviceOverlay, {
[styles.overlayEnabled]: this.state.screenshotInProgress,
})}
style={deviceStyles}
/>
<div
className={cx(styles.deviceOverlay, {
[styles.overlayEnabled]: this.state.errorCode,
})}
style={deviceStyles}
>
<p>ERROR: {this.state.errorCode}</p>
<p className={cx(styles.errorDesc)}>{this.state.errorDesc}</p>
</div>
<webview
ref={this.webviewRef}
preload="./preload.js"
className={cx(styles.device)}
src={browser.address || 'about:blank'}
useragent={device.useragent}
style={deviceStyles}
/>
</div>
</div>
);
}
);
}
}
export default WebView;

View file

@ -7,35 +7,47 @@ import Renderer from './';
const testSrc = 'https://testUrl.com';
const testDevice1 = {
name: 'testDevice1',
width: 100,
height: 100,
name: 'testDevice1',
width: 100,
height: 100,
};
describe('<Renderer />', () => {
it('Renders the header and the iframe', () => {
const wrapper = shallow(<Renderer src={testSrc} device={testDevice1} />);
expect(wrapper.find('iframe')).to.have.lengthOf(1);
expect(wrapper.find('h2')).to.have.lengthOf(1);
});
it('Renders the header and the iframe', () => {
const wrapper = shallow(
<Renderer src={testSrc} device={testDevice1} />
);
expect(wrapper.find('iframe')).to.have.lengthOf(1);
expect(wrapper.find('h2')).to.have.lengthOf(1);
});
it('Renders the header with the device name', () => {
const wrapper = shallow(<Renderer src={testSrc} device={testDevice1} />);
expect(wrapper.find('h2').text()).to.equal(testDevice1.name);
});
it('Renders the header with the device name', () => {
const wrapper = shallow(
<Renderer src={testSrc} device={testDevice1} />
);
expect(wrapper.find('h2').text()).to.equal(testDevice1.name);
});
it('Renders the iframe with the given device dimensions', () => {
const wrapper = shallow(<Renderer src={testSrc} device={testDevice1} />);
expect(wrapper.find('iframe').prop('width')).to.equal(testDevice1.width);
expect(wrapper.find('iframe').prop('height')).to.equal(testDevice1.height);
});
it('Renders the iframe with the given device dimensions', () => {
const wrapper = shallow(
<Renderer src={testSrc} device={testDevice1} />
);
expect(wrapper.find('iframe').prop('width')).to.equal(
testDevice1.width
);
expect(wrapper.find('iframe').prop('height')).to.equal(
testDevice1.height
);
});
it('Renders the iframe with the given url', () => {
const wrapper = shallow(<Renderer src={testSrc} device={testDevice1} />);
expect(wrapper.find('iframe').prop('src')).to.equal(testSrc);
});
it('Renders the iframe with the given url', () => {
const wrapper = shallow(
<Renderer src={testSrc} device={testDevice1} />
);
expect(wrapper.find('iframe').prop('src')).to.equal(testSrc);
});
/*it('Calls the callback with a number value', () => {
/*it('Calls the callback with a number value', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');

View file

@ -14,23 +14,23 @@ import PromiseWorker from 'promise-worker';
const mergeImg = Promise.promisifyAll(_mergeImg);
export const captureFullPage = async (
address,
device,
webView,
createSeparateDir,
now
address,
device,
webView,
createSeparateDir,
now
) => {
const worker = new Worker('./imageWorker.js');
const promiseWorker = new PromiseWorker(worker);
const toastId = toast.info(
<NotificationMessage
spinner={true}
message={`Capturing ${device.name} screenshot...`}
/>,
{autoClose: false}
);
//Hiding scrollbars in the screenshot
await webView.insertCSS(`
const worker = new Worker('./imageWorker.js');
const promiseWorker = new PromiseWorker(worker);
const toastId = toast.info(
<NotificationMessage
spinner={true}
message={`Capturing ${device.name} screenshot...`}
/>,
{autoClose: false}
);
//Hiding scrollbars in the screenshot
await webView.insertCSS(`
.responsivelyApp__ScreenshotInProgress::-webkit-scrollbar {
display: none;
}
@ -40,18 +40,18 @@ export const captureFullPage = async (
}
`);
//Get the windows's scroll details
let scrollX = 0;
let scrollY = 0;
let pageX = 0;
let pageY = 0;
const {
previousScrollPosition,
scrollHeight,
viewPortHeight,
scrollWidth,
viewPortWidth,
} = await webView.executeJavaScript(`
//Get the windows's scroll details
let scrollX = 0;
let scrollY = 0;
let pageX = 0;
let pageY = 0;
const {
previousScrollPosition,
scrollHeight,
viewPortHeight,
scrollWidth,
viewPortWidth,
} = await webView.executeJavaScript(`
document.body.classList.add('responsivelyApp__ScreenshotInProgress');
responsivelyApp.screenshotVar = {
previousScrollPosition : {
@ -66,137 +66,137 @@ export const captureFullPage = async (
responsivelyApp.screenshotVar;
`);
let images = [];
let images = [];
for (
let pageY = 0;
scrollY < scrollHeight;
pageY++, scrollY = viewPortHeight * pageY
) {
scrollX = 0;
const columnImages = [];
for (
let pageX = 0;
scrollX < scrollWidth;
pageX++, scrollX = viewPortWidth * pageX
let pageY = 0;
scrollY < scrollHeight;
pageY++, scrollY = viewPortHeight * pageY
) {
await webView.executeJavaScript(`
scrollX = 0;
const columnImages = [];
for (
let pageX = 0;
scrollX < scrollWidth;
pageX++, scrollX = viewPortWidth * pageX
) {
await webView.executeJavaScript(`
window.scrollTo(${scrollX}, ${scrollY})
responsivelyApp.hideFixedPositionElementsForScreenshot();
`);
await _delay(200);
const options = {
x: 0,
y: 0,
width: viewPortWidth,
height: viewPortHeight,
};
if (scrollX + viewPortWidth > scrollWidth) {
options.width = scrollWidth - scrollX;
options.x = viewPortWidth - options.width;
}
if (scrollY + viewPortHeight > scrollHeight) {
options.height = scrollHeight - scrollY;
options.y = viewPortHeight - options.height;
}
const image = await _takeSnapshot(webView, options);
columnImages.push(image);
await _delay(200);
const options = {
x: 0,
y: 0,
width: viewPortWidth,
height: viewPortHeight,
};
if (scrollX + viewPortWidth > scrollWidth) {
options.width = scrollWidth - scrollX;
options.x = viewPortWidth - options.width;
}
if (scrollY + viewPortHeight > scrollHeight) {
options.height = scrollHeight - scrollY;
options.y = viewPortHeight - options.height;
}
const image = await _takeSnapshot(webView, options);
columnImages.push(image);
}
const pngs = columnImages.map(img => img.toPNG());
images.push(
await promiseWorker.postMessage(
{
images: pngs,
direction: 'horizontal',
},
[...pngs]
)
);
}
const pngs = columnImages.map(img => img.toPNG());
images.push(
await promiseWorker.postMessage(
{
images: pngs,
direction: 'horizontal',
},
[...pngs]
)
);
}
webView.executeJavaScript(`
webView.executeJavaScript(`
window.scrollTo(${JSON.stringify(previousScrollPosition)});
document.body.classList.remove('responsivelyApp__ScreenshotInProgress');
responsivelyApp.unHideElementsHiddenForScreenshot();
`);
toast.update(toastId, {
render: (
<NotificationMessage
spinner={true}
message={`Processing ${device.name} screenshot...`}
/>
),
type: toast.TYPE.INFO,
});
const resultFilename = _getScreenshotFileName(
address,
device,
now,
createSeparateDir
);
const mergedImage = await promiseWorker.postMessage({
images,
direction: 'vertical',
resultFilename,
});
toast.update(toastId, {
render: (
<NotificationMessage
tick={true}
message={`${device.name} screenshot taken!`}
/>
),
type: toast.TYPE.INFO,
autoClose: 2000,
});
await _delay(250);
shell.showItemInFolder(path.join(resultFilename.dir, resultFilename.file));
toast.update(toastId, {
render: (
<NotificationMessage
spinner={true}
message={`Processing ${device.name} screenshot...`}
/>
),
type: toast.TYPE.INFO,
});
const resultFilename = _getScreenshotFileName(
address,
device,
now,
createSeparateDir
);
const mergedImage = await promiseWorker.postMessage({
images,
direction: 'vertical',
resultFilename,
});
toast.update(toastId, {
render: (
<NotificationMessage
tick={true}
message={`${device.name} screenshot taken!`}
/>
),
type: toast.TYPE.INFO,
autoClose: 2000,
});
await _delay(250);
shell.showItemInFolder(path.join(resultFilename.dir, resultFilename.file));
};
const _delay = ms =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(), ms);
});
new Promise((resolve, reject) => {
setTimeout(() => resolve(), ms);
});
const _takeSnapshot = (webView, options) => {
return webView.getWebContents().capturePage(options);
return webView.getWebContents().capturePage(options);
};
function _getScreenshotFileName(
address,
device,
now = new Date(),
createSeparateDir
address,
device,
now = new Date(),
createSeparateDir
) {
const dateString = `${now
.toLocaleDateString()
.split('/')
.reverse()
.join('-')} at ${now
.toLocaleTimeString([], {hour12: true})
.replace(/\:/g, '.')
.toUpperCase()}`;
const directoryPath = createSeparateDir ? `${dateString}/` : '';
return {
dir: path.join(
os.homedir(),
`Desktop/Responsively-Screenshots`,
directoryPath
),
file: `${_getWebsiteName(address)} - ${device.name.replace(
/\//g,
'-'
)} - ${dateString}.png`,
};
const dateString = `${now
.toLocaleDateString()
.split('/')
.reverse()
.join('-')} at ${now
.toLocaleTimeString([], {hour12: true})
.replace(/\:/g, '.')
.toUpperCase()}`;
const directoryPath = createSeparateDir ? `${dateString}/` : '';
return {
dir: path.join(
os.homedir(),
`Desktop/Responsively-Screenshots`,
directoryPath
),
file: `${_getWebsiteName(address)} - ${device.name.replace(
/\//g,
'-'
)} - ${dateString}.png`,
};
}
const _getWebsiteName = address => {
let domain = new URL(address).hostname;
domain = domain.replace('www.', '');
const dotIndex = domain.indexOf('.');
if (dotIndex > -1) {
domain = domain.substr(0, domain.indexOf('.'));
}
return domain.charAt(0).toUpperCase() + domain.slice(1);
let domain = new URL(address).hostname;
domain = domain.replace('www.', '');
const dotIndex = domain.indexOf('.');
if (dotIndex > -1) {
domain = domain.substr(0, domain.indexOf('.'));
}
return domain.charAt(0).toUpperCase() + domain.slice(1);
};

View file

@ -18,72 +18,72 @@ import {iconsColor} from '../../constants/colors';
import {Tooltip} from '@material-ui/core';
const marks = [
{
value: 25,
label: '25%',
},
{
value: 50,
label: '50%',
},
{
value: 100,
label: '100%',
},
{
value: 200,
label: '200%',
},
{
value: 25,
label: '25%',
},
{
value: 50,
label: '50%',
},
{
value: 100,
label: '100%',
},
{
value: 200,
label: '200%',
},
];
export default function BrowserZoom(props) {
const [showExpanded, setShowExpanded] = useState(false);
const zoomRef = useRef();
const handleClickOutside = event => {
if (!showExpanded) {
return;
}
if (zoomRef.current && !zoomRef.current.contains(event.target)) {
setShowExpanded(false);
}
};
useEffect(() => {
// Bind the event listener
document.addEventListener('mousedown', handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener('mousedown', handleClickOutside);
const [showExpanded, setShowExpanded] = useState(false);
const zoomRef = useRef();
const handleClickOutside = event => {
if (!showExpanded) {
return;
}
if (zoomRef.current && !zoomRef.current.contains(event.target)) {
setShowExpanded(false);
}
};
});
useEffect(() => {
// Bind the event listener
document.addEventListener('mousedown', handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener('mousedown', handleClickOutside);
};
});
const _zoomChange = (_, [action]) => {
switch (action) {
case 'zoomIn':
return props.onZoomChange(props.browser.zoomLevel + 0.1);
case 'zoomOut':
return props.onZoomChange(props.browser.zoomLevel - 0.1);
}
};
const _zoomChange = (_, [action]) => {
switch (action) {
case 'zoomIn':
return props.onZoomChange(props.browser.zoomLevel + 0.1);
case 'zoomOut':
return props.onZoomChange(props.browser.zoomLevel - 0.1);
}
};
const value = Math.round(props.browser.zoomLevel * 100);
const value = Math.round(props.browser.zoomLevel * 100);
return (
<div
ref={zoomRef}
className={cx(
commonStyles.icons,
commonStyles.enabled,
styles.zoomSlider,
'MuiGrid-item',
'MuiGrid-root'
)}
>
<Tooltip title="Zoom In/Out">
<div onClick={() => setShowExpanded(!showExpanded)}>
<ZoomIcon width={25} color={iconsColor} />
</div>
</Tooltip>
{/*<Grid container spacing={1}>
return (
<div
ref={zoomRef}
className={cx(
commonStyles.icons,
commonStyles.enabled,
styles.zoomSlider,
'MuiGrid-item',
'MuiGrid-root'
)}
>
<Tooltip title="Zoom In/Out">
<div onClick={() => setShowExpanded(!showExpanded)}>
<ZoomIcon width={25} color={iconsColor} />
</div>
</Tooltip>
{/*<Grid container spacing={1}>
<Grid item>
<ZoomOutIcon />
</Grid>
@ -100,23 +100,35 @@ export default function BrowserZoom(props) {
<ZoomInIcon />
</Grid>
</Grid>*/}
<div
className={cx(styles.zoomControls, {
[commonStyles.hidden]: !showExpanded,
})}
>
<ToggleButtonGroup value={[]} onChange={_zoomChange}>
<ToggleButton value="zoomOut" disabled={value === 20} disableRipple>
&ndash;
</ToggleButton>
<ToggleButton value="value" disabled className={styles.zoomValue}>
{value}%
</ToggleButton>
<ToggleButton value="zoomIn" disabled={value === 200} disableRipple>
+
</ToggleButton>
</ToggleButtonGroup>
</div>
</div>
);
<div
className={cx(styles.zoomControls, {
[commonStyles.hidden]: !showExpanded,
})}
>
<ToggleButtonGroup value={[]} onChange={_zoomChange}>
<ToggleButton
value="zoomOut"
disabled={value === 20}
disableRipple
>
&ndash;
</ToggleButton>
<ToggleButton
value="value"
disabled
className={styles.zoomValue}
>
{value}%
</ToggleButton>
<ToggleButton
value="zoomIn"
disabled={value === 200}
disableRipple
>
+
</ToggleButton>
</ToggleButtonGroup>
</div>
</div>
);
}

View file

@ -8,20 +8,20 @@ import BrowserZoom from './';
import Slider from '@material-ui/core/Slider';
describe('<BrowserZoom />', () => {
it('Renders label and the slider component ', () => {
const wrapper = shallow(<BrowserZoom />);
expect(wrapper.find(Slider)).to.have.lengthOf(1);
});
it('Renders label and the slider component ', () => {
const wrapper = shallow(<BrowserZoom />);
expect(wrapper.find(Slider)).to.have.lengthOf(1);
});
it('Calls the callback on slider change', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');
wrapper.find('.MuiSlider-thumb').simulate('mouseup');
expect(onChange).to.have.property('callCount', 1);
});
it('Calls the callback on slider change', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');
wrapper.find('.MuiSlider-thumb').simulate('mouseup');
expect(onChange).to.have.property('callCount', 1);
});
/*it('Calls the callback with a number value', () => {
/*it('Calls the callback with a number value', () => {
const onChange = sinon.spy();
const wrapper = mount(<BrowserZoom onChange={onChange} />);
wrapper.find('.MuiSlider-thumb').simulate('mousedown');

View file

@ -1,17 +1,17 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 24 24"
>
<path d="M14.94,5.19A4.38,4.38,0,0,0,16,2,4.44,4.44,0,0,0,13,3.52,4.17,4.17,0,0,0,12,6.61,3.69,3.69,0,0,0,14.94,5.19Zm2.52,7.44a4.51,4.51,0,0,1,2.16-3.81,4.66,4.66,0,0,0-3.66-2c-1.56-.16-3,.91-3.83.91s-2-.89-3.3-.87A4.92,4.92,0,0,0,4.69,9.39C2.93,12.45,4.24,17,6,19.47,6.8,20.68,7.8,22.05,9.12,22s1.75-.82,3.28-.82,2,.82,3.3.79,2.22-1.24,3.06-2.45a11,11,0,0,0,1.38-2.85A4.41,4.41,0,0,1,17.46,12.63Z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 24 24"
>
<path d="M14.94,5.19A4.38,4.38,0,0,0,16,2,4.44,4.44,0,0,0,13,3.52,4.17,4.17,0,0,0,12,6.61,3.69,3.69,0,0,0,14.94,5.19Zm2.52,7.44a4.51,4.51,0,0,1,2.16-3.81,4.66,4.66,0,0,0-3.66-2c-1.56-.16-3,.91-3.83.91s-2-.89-3.3-.87A4.92,4.92,0,0,0,4.69,9.39C2.93,12.45,4.24,17,6,19.47,6.8,20.68,7.8,22.05,9.12,22s1.75-.82,3.28-.82,2,.82,3.3.79,2.22-1.24,3.06-2.45a11,11,0,0,0,1.38-2.85A4.41,4.41,0,0,1,17.46,12.63Z" />
</svg>
</Fragment>
);

View file

@ -1,17 +1,17 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 24 24"
>
<path d="M17,11H9.41l3.3-3.29a1,1,0,1,0-1.42-1.42l-5,5a1,1,0,0,0-.21.33,1,1,0,0,0,0,.76,1,1,0,0,0,.21.33l5,5a1,1,0,0,0,1.42,0,1,1,0,0,0,0-1.42L9.41,13H17a1,1,0,0,0,0-2Z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 24 24"
>
<path d="M17,11H9.41l3.3-3.29a1,1,0,1,0-1.42-1.42l-5,5a1,1,0,0,0-.21.33,1,1,0,0,0,0,.76,1,1,0,0,0,.21.33l5,5a1,1,0,0,0,1.42,0,1,1,0,0,0,0-1.42L9.41,13H17a1,1,0,0,0,0-2Z" />
</svg>
</Fragment>
);

View file

@ -1,17 +1,17 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 24 24"
>
<path d="M17.92,11.62a1,1,0,0,0-.21-.33l-5-5a1,1,0,0,0-1.42,1.42L14.59,11H7a1,1,0,0,0,0,2h7.59l-3.3,3.29a1,1,0,0,0,0,1.42,1,1,0,0,0,1.42,0l5-5a1,1,0,0,0,.21-.33A1,1,0,0,0,17.92,11.62Z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 24 24"
>
<path d="M17.92,11.62a1,1,0,0,0-.21-.33l-5-5a1,1,0,0,0-1.42,1.42L14.59,11H7a1,1,0,0,0,0,2h7.59l-3.3,3.29a1,1,0,0,0,0,1.42,1,1,0,0,0,1.42,0l5-5a1,1,0,0,0,.21-.33A1,1,0,0,0,17.92,11.62Z" />
</svg>
</Fragment>
);

View file

@ -1,8 +1,8 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
{/*<svg
<Fragment>
{/*<svg
width={width}
height={height}
fill={color}
@ -11,25 +11,25 @@ export default ({width, height, color, padding, margin}) => (
>
<path d="M9.71,6.29a1,1,0,0,0-1.42,0l-5,5a1,1,0,0,0,0,1.42l5,5a1,1,0,0,0,1.42,0,1,1,0,0,0,0-1.42L5.41,12l4.3-4.29A1,1,0,0,0,9.71,6.29Zm11,5-5-5a1,1,0,0,0-1.42,1.42L18.59,12l-4.3,4.29a1,1,0,0,0,0,1.42,1,1,0,0,0,1.42,0l5-5A1,1,0,0,0,20.71,11.29Z" />
</svg>*/}
<svg
width={width}
height={height}
fill={color}
style={{padding, margin}}
viewBox="0 0 100 100"
x="0px"
y="0px"
>
<g data-name="Group">
<polygon
data-name="Path"
points="39.5 23.6 13.1 50 39.5 76.4 42.4 73.6 18.8 50 42.4 26.4 39.5 23.6"
/>
<polygon
data-name="Path"
points="60.5 76.4 86.9 50 60.5 23.6 57.6 26.4 81.2 50 57.6 73.6 60.5 76.4"
/>
</g>
</svg>
</Fragment>
<svg
width={width}
height={height}
fill={color}
style={{padding, margin}}
viewBox="0 0 100 100"
x="0px"
y="0px"
>
<g data-name="Group">
<polygon
data-name="Path"
points="39.5 23.6 13.1 50 39.5 76.4 42.4 73.6 18.8 50 42.4 26.4 39.5 23.6"
/>
<polygon
data-name="Path"
points="60.5 76.4 86.9 50 60.5 23.6 57.6 26.4 81.2 50 57.6 73.6 60.5 76.4"
/>
</g>
</svg>
</Fragment>
);

View file

@ -1,16 +1,16 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M13.41,12l4.3-4.29a1,1,0,1,0-1.42-1.42L12,10.59,7.71,6.29A1,1,0,0,0,6.29,7.71L10.59,12l-4.3,4.29a1,1,0,0,0,0,1.42,1,1,0,0,0,1.42,0L12,13.41l4.29,4.3a1,1,0,0,0,1.42,0,1,1,0,0,0,0-1.42Z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M13.41,12l4.3-4.29a1,1,0,1,0-1.42-1.42L12,10.59,7.71,6.29A1,1,0,0,0,6.29,7.71L10.59,12l-4.3,4.29a1,1,0,0,0,0,1.42,1,1,0,0,0,1.42,0L12,13.41l4.29,4.3a1,1,0,0,0,1.42,0,1,1,0,0,0,0-1.42Z" />
</svg>
</Fragment>
);

View file

@ -1,8 +1,8 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
{/*<svg
<Fragment>
{/*<svg
height={height}
width={width}
fill={color}
@ -55,39 +55,39 @@ export default ({width, height, color, padding, margin}) => (
<g />
<g />
</svg>*/}
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 100 100"
x="0px"
y="0px"
>
<g data-name="Group">
<path
data-name="Compound Path"
d="M8.8,62.2a7.2,7.2,0,0,0,2.1,5.2L32.7,89.1a7.3,7.3,0,0,0,10.3,0L89.1,43a7.3,7.3,0,0,0,0-10.3L67.3,10.9a7.3,7.3,0,0,0-10.3,0L10.9,57A7.2,7.2,0,0,0,8.8,62.2ZM48,78.4,21.6,52,54.9,18.7,81.3,45.1ZM64.5,13.7,86.3,35.5a3.3,3.3,0,0,1,0,4.7l-2.2,2.2L57.7,15.9l2.2-2.2A3.4,3.4,0,0,1,64.5,13.7ZM13.7,59.9l5.1-5.1L45.2,81.2l-5.1,5.1a3.4,3.4,0,0,1-4.7,0L13.7,64.5a3.3,3.3,0,0,1,0-4.7Z"
/>
<rect
data-name="Path"
x="26.4"
y="69.2"
width="4"
height="4.88"
transform="translate(-42.4 41.1) rotate(-45)"
/>
<path
data-name="Path"
d="M92.8,68.1A14.2,14.2,0,0,1,78.6,82.3H65.8l7.4-8.7-3-2.6L58.9,84.3,70.2,97.5l3-2.6-7.4-8.6H78.6A18.2,18.2,0,0,0,96.8,68.1Z"
/>
<path
data-name="Path"
d="M21.4,17.7H34.2l-7.4,8.7,3,2.6L41.1,15.7,29.8,2.5l-3,2.6,7.4,8.6H21.4A18.2,18.2,0,0,0,3.2,31.9h4A14.2,14.2,0,0,1,21.4,17.7Z"
/>
</g>
</svg>
</Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 100 100"
x="0px"
y="0px"
>
<g data-name="Group">
<path
data-name="Compound Path"
d="M8.8,62.2a7.2,7.2,0,0,0,2.1,5.2L32.7,89.1a7.3,7.3,0,0,0,10.3,0L89.1,43a7.3,7.3,0,0,0,0-10.3L67.3,10.9a7.3,7.3,0,0,0-10.3,0L10.9,57A7.2,7.2,0,0,0,8.8,62.2ZM48,78.4,21.6,52,54.9,18.7,81.3,45.1ZM64.5,13.7,86.3,35.5a3.3,3.3,0,0,1,0,4.7l-2.2,2.2L57.7,15.9l2.2-2.2A3.4,3.4,0,0,1,64.5,13.7ZM13.7,59.9l5.1-5.1L45.2,81.2l-5.1,5.1a3.4,3.4,0,0,1-4.7,0L13.7,64.5a3.3,3.3,0,0,1,0-4.7Z"
/>
<rect
data-name="Path"
x="26.4"
y="69.2"
width="4"
height="4.88"
transform="translate(-42.4 41.1) rotate(-45)"
/>
<path
data-name="Path"
d="M92.8,68.1A14.2,14.2,0,0,1,78.6,82.3H65.8l7.4-8.7-3-2.6L58.9,84.3,70.2,97.5l3-2.6-7.4-8.6H78.6A18.2,18.2,0,0,0,96.8,68.1Z"
/>
<path
data-name="Path"
d="M21.4,17.7H34.2l-7.4,8.7,3,2.6L41.1,15.7,29.8,2.5l-3,2.6,7.4,8.6H21.4A18.2,18.2,0,0,0,3.2,31.9h4A14.2,14.2,0,0,1,21.4,17.7Z"
/>
</g>
</svg>
</Fragment>
);

View file

@ -1,23 +1,23 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 100"
enableBackground="new 0 0 100 100"
>
<path d="M17.914,60.871v9.576h3.573v-9.576H17.914z M66.886,25.279v9.703h3.573V17.425c0-2.659-2.181-4.82-4.842-4.82h-3.075v7.853 C64.975,20.729,66.886,22.765,66.886,25.279z" />
<path d="M65.617,12.605H22.775c-2.68,0-4.861,2.161-4.861,4.82v63.183c0,2.68,2.181,4.842,4.841,4.842h32.91v-8.436H26.348 c-1.495,0-2.844-0.664-3.739-1.723v-0.023c-0.706-0.85-1.122-1.91-1.122-3.074V25.279c0-2.68,2.182-4.841,4.861-4.841h35.674 c0.166,0,0.355,0,0.52,0.02c2.433,0.271,4.344,2.307,4.344,4.821v9.703h3.573V17.425C70.459,14.766,68.278,12.605,65.617,12.605z M44.425,78.676c1.495,0,2.722,1.207,2.722,2.701c0,1.516-1.227,2.721-2.722,2.721c-1.516,0-2.721-1.205-2.721-2.721 C41.704,79.883,42.909,78.676,44.425,78.676z" />
<path d="M82.239,37.995h-20.88c-1.769,0-3.181,1.413-3.181,3.18v41.513c0,1.203,0.646,2.223,1.622,2.762 c0.456,0.27,0.996,0.416,1.559,0.416h20.88c1.765,0,3.2-1.414,3.2-3.178V41.174C85.439,39.407,84.004,37.995,82.239,37.995z M60.527,46.326c0-1.351,0.832-2.493,2.015-2.95c0.354-0.146,0.749-0.229,1.165-0.229h16.185c1.766,0,3.2,1.415,3.2,3.179v30.813 c0,1.766-1.435,3.18-3.2,3.18H63.707c-1.746,0-3.18-1.414-3.18-3.18V46.326z M71.664,84.971c-0.976,0-1.767-0.789-1.767-1.787 c0-0.166,0.021-0.311,0.083-0.455c0.022-0.189,0.105-0.355,0.208-0.5c0,0,0-0.02,0.021-0.041c0.312-0.457,0.852-0.77,1.454-0.77 c0.997,0,1.786,0.791,1.786,1.766C73.45,84.182,72.661,84.971,71.664,84.971z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 100"
enableBackground="new 0 0 100 100"
>
<path d="M17.914,60.871v9.576h3.573v-9.576H17.914z M66.886,25.279v9.703h3.573V17.425c0-2.659-2.181-4.82-4.842-4.82h-3.075v7.853 C64.975,20.729,66.886,22.765,66.886,25.279z" />
<path d="M65.617,12.605H22.775c-2.68,0-4.861,2.161-4.861,4.82v63.183c0,2.68,2.181,4.842,4.841,4.842h32.91v-8.436H26.348 c-1.495,0-2.844-0.664-3.739-1.723v-0.023c-0.706-0.85-1.122-1.91-1.122-3.074V25.279c0-2.68,2.182-4.841,4.861-4.841h35.674 c0.166,0,0.355,0,0.52,0.02c2.433,0.271,4.344,2.307,4.344,4.821v9.703h3.573V17.425C70.459,14.766,68.278,12.605,65.617,12.605z M44.425,78.676c1.495,0,2.722,1.207,2.722,2.701c0,1.516-1.227,2.721-2.722,2.721c-1.516,0-2.721-1.205-2.721-2.721 C41.704,79.883,42.909,78.676,44.425,78.676z" />
<path d="M82.239,37.995h-20.88c-1.769,0-3.181,1.413-3.181,3.18v41.513c0,1.203,0.646,2.223,1.622,2.762 c0.456,0.27,0.996,0.416,1.559,0.416h20.88c1.765,0,3.2-1.414,3.2-3.178V41.174C85.439,39.407,84.004,37.995,82.239,37.995z M60.527,46.326c0-1.351,0.832-2.493,2.015-2.95c0.354-0.146,0.749-0.229,1.165-0.229h16.185c1.766,0,3.2,1.415,3.2,3.179v30.813 c0,1.766-1.435,3.18-3.2,3.18H63.707c-1.746,0-3.18-1.414-3.18-3.18V46.326z M71.664,84.971c-0.976,0-1.767-0.789-1.767-1.787 c0-0.166,0.021-0.311,0.083-0.455c0.022-0.189,0.105-0.355,0.208-0.5c0,0,0-0.02,0.021-0.041c0.312-0.457,0.852-0.77,1.454-0.77 c0.997,0,1.786,0.791,1.786,1.766C73.45,84.182,72.661,84.971,71.664,84.971z" />
</svg>
</Fragment>
);

View file

@ -1,44 +1,44 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
viewBox="0 0 1240 1240"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<g
id="chevronsLeft"
stroke="none"
strokeWidth="1"
fill="none"
fillRule="evenodd"
strokeLinecap="round"
strokeLinejoin="round"
>
<g
id="Group"
transform="translate(619.500000, 620.000000) rotate(90.000000) translate(-619.500000, -620.000000) translate(409.000000, 406.000000)"
stroke="white"
strokeWidth="50"
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
viewBox="0 0 1240 1240"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<polyline
id="Path"
transform="translate(210.500000, 321.000000) scale(1, -1) translate(-210.500000, -321.000000) "
points="0 428 210.5 214 421 428"
/>
<polyline
id="Path"
transform="translate(210.500000, 107.000000) scale(1, -1) translate(-210.500000, -107.000000) "
points="0 214 210.5 0 421 214"
/>
</g>
</g>
</svg>
</Fragment>
<g
id="chevronsLeft"
stroke="none"
strokeWidth="1"
fill="none"
fillRule="evenodd"
strokeLinecap="round"
strokeLinejoin="round"
>
<g
id="Group"
transform="translate(619.500000, 620.000000) rotate(90.000000) translate(-619.500000, -620.000000) translate(409.000000, 406.000000)"
stroke="white"
strokeWidth="50"
>
<polyline
id="Path"
transform="translate(210.500000, 321.000000) scale(1, -1) translate(-210.500000, -321.000000) "
points="0 428 210.5 214 421 428"
/>
<polyline
id="Path"
transform="translate(210.500000, 107.000000) scale(1, -1) translate(-210.500000, -107.000000) "
points="0 214 210.5 0 421 214"
/>
</g>
</g>
</svg>
</Fragment>
);

View file

@ -1,20 +1,20 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
width={width}
height={height}
fill={color}
version="1.1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 129 129"
xmlnsXlink="http://www.w3.org/1999/xlink"
enableBackground="new 0 0 129 129"
>
<g>
<path d="m40.4,121.3c-0.8,0.8-1.8,1.2-2.9,1.2s-2.1-0.4-2.9-1.2c-1.6-1.6-1.6-4.2 0-5.8l51-51-51-51c-1.6-1.6-1.6-4.2 0-5.8 1.6-1.6 4.2-1.6 5.8,0l53.9,53.9c1.6,1.6 1.6,4.2 0,5.8l-53.9,53.9z" />
</g>
</svg>
</Fragment>
<Fragment>
<svg
width={width}
height={height}
fill={color}
version="1.1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 129 129"
xmlnsXlink="http://www.w3.org/1999/xlink"
enableBackground="new 0 0 129 129"
>
<g>
<path d="m40.4,121.3c-0.8,0.8-1.8,1.2-2.9,1.2s-2.1-0.4-2.9-1.2c-1.6-1.6-1.6-4.2 0-5.8l51-51-51-51c-1.6-1.6-1.6-4.2 0-5.8 1.6-1.6 4.2-1.6 5.8,0l53.9,53.9c1.6,1.6 1.6,4.2 0,5.8l-53.9,53.9z" />
</g>
</svg>
</Fragment>
);

View file

@ -1,22 +1,22 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
version="1.1"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 63.699 63.699"
xmlSpace="preserve"
>
<g>
<path
d="M63.663,29.424c-0.143-1.093-0.701-2.065-1.575-2.737l-11.715-9.021V8.608c0-2.275-1.851-4.126-4.125-4.126
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
version="1.1"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 63.699 63.699"
xmlSpace="preserve"
>
<g>
<path
d="M63.663,29.424c-0.143-1.093-0.701-2.065-1.575-2.737l-11.715-9.021V8.608c0-2.275-1.851-4.126-4.125-4.126
c-2.273,0-4.125,1.851-4.125,4.126v2.705l-7.758-5.975c-0.718-0.551-1.612-0.856-2.517-0.856c-0.906,0-1.801,0.304-2.519,0.857
L1.606,26.687c-1.802,1.389-2.139,3.983-0.751,5.785c0.788,1.022,1.979,1.608,3.271,1.608c0.664,0,1.302-0.153,1.88-0.451V55.09
c0,2.275,1.851,4.127,4.126,4.127h18.534V39.732h6.351v19.482h18.271c2.274,0,4.125-1.85,4.125-4.127V33.472
@ -26,23 +26,23 @@ export default ({width, height, color, padding, margin}) => (
c-0.059,0-0.167-0.017-0.248-0.121c-0.065-0.084-0.07-0.171-0.062-0.229c0.007-0.058,0.034-0.141,0.118-0.205L31.661,8.363
c0.138-0.105,0.239-0.106,0.379,0l13.899,10.703V8.608c0-0.172,0.14-0.311,0.311-0.311s0.312,0.139,0.312,0.311v10.935
l13.205,10.166c0.084,0.064,0.108,0.147,0.116,0.205C59.891,29.975,59.885,30.062,59.819,30.144z"
/>
</g>
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
</svg>
</Fragment>
/>
</g>
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
</svg>
</Fragment>
);

View file

@ -1,51 +1,51 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
stroke={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 66 66"
>
<defs>
<clipPath id="_clipPath_1jbp17xur21ul8z5IuRkxmo4gJ5cDGqz">
<rect width="66" height="66" />
</clipPath>
</defs>
<g clipPath="url(#_clipPath_1jbp17xur21ul8z5IuRkxmo4gJ5cDGqz)">
<g>
<path d=" M 47.973 42.767 C 47.865 41.943 47.445 41.211 46.786 40.704 L 37.958 33.907 L 37.958 27.081 C 37.958 25.367 36.564 23.972 34.85 23.972 C 33.137 23.972 31.742 25.367 31.742 27.081 L 31.742 29.119 L 25.896 24.617 C 25.355 24.202 24.681 23.972 23.999 23.972 C 23.316 23.972 22.642 24.201 22.101 24.618 L 1.21 40.704 C -0.147 41.751 -0.401 43.706 0.645 45.064 C 1.238 45.834 2.136 46.275 3.109 46.275 C 3.61 46.275 4.091 46.16 4.526 45.935 L 4.526 62.107 C 4.526 63.821 5.921 65.217 7.635 65.217 L 21.601 65.217 L 21.601 50.534 L 26.387 50.534 L 26.387 65.215 L 40.155 65.215 C 41.869 65.215 43.263 63.821 43.263 62.105 L 43.263 45.817 C 43.752 46.118 44.309 46.275 44.889 46.275 C 45.86 46.275 46.759 45.834 47.353 45.065 C 47.861 44.407 48.082 43.591 47.973 42.767 Z M 45.076 43.309 C 45.016 43.388 44.934 43.401 44.89 43.401 C 44.838 43.401 44.791 43.385 44.749 43.352 L 40.39 39.997 L 40.39 62.107 C 40.39 62.237 40.285 62.342 40.156 62.342 L 29.262 62.342 L 29.263 47.66 L 18.727 47.66 L 18.727 62.342 L 7.635 62.342 C 7.506 62.342 7.402 62.236 7.402 62.107 L 7.402 40.156 L 3.252 43.351 C 3.21 43.384 3.162 43.4 3.11 43.4 C 3.066 43.4 2.984 43.388 2.923 43.309 C 2.874 43.246 2.871 43.18 2.877 43.137 C 2.882 43.093 2.902 43.03 2.965 42.982 L 23.858 26.896 C 23.962 26.817 24.038 26.817 24.144 26.896 L 34.617 34.962 L 34.617 27.081 C 34.617 26.951 34.723 26.847 34.852 26.847 C 34.98 26.847 35.087 26.951 35.087 27.081 L 35.087 35.321 L 45.037 42.982 C 45.1 43.03 45.119 43.092 45.125 43.136 C 45.131 43.182 45.126 43.248 45.076 43.309 Z " />
</g>
<g>
<line
x1="51"
y1="2"
x2="51"
y2="28"
vectorEffect="non-scaling-stroke"
strokeWidth="1"
strokeLinejoin="miter"
strokeLinecap="round"
strokeMiterlimit="3"
/>
<line
x1="64"
y1="15"
x2="38"
y2="15"
vectorEffect="non-scaling-stroke"
strokeWidth="1"
strokeLinejoin="miter"
strokeLinecap="round"
strokeMiterlimit="3"
/>
</g>
</g>
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
stroke={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 66 66"
>
<defs>
<clipPath id="_clipPath_1jbp17xur21ul8z5IuRkxmo4gJ5cDGqz">
<rect width="66" height="66" />
</clipPath>
</defs>
<g clipPath="url(#_clipPath_1jbp17xur21ul8z5IuRkxmo4gJ5cDGqz)">
<g>
<path d=" M 47.973 42.767 C 47.865 41.943 47.445 41.211 46.786 40.704 L 37.958 33.907 L 37.958 27.081 C 37.958 25.367 36.564 23.972 34.85 23.972 C 33.137 23.972 31.742 25.367 31.742 27.081 L 31.742 29.119 L 25.896 24.617 C 25.355 24.202 24.681 23.972 23.999 23.972 C 23.316 23.972 22.642 24.201 22.101 24.618 L 1.21 40.704 C -0.147 41.751 -0.401 43.706 0.645 45.064 C 1.238 45.834 2.136 46.275 3.109 46.275 C 3.61 46.275 4.091 46.16 4.526 45.935 L 4.526 62.107 C 4.526 63.821 5.921 65.217 7.635 65.217 L 21.601 65.217 L 21.601 50.534 L 26.387 50.534 L 26.387 65.215 L 40.155 65.215 C 41.869 65.215 43.263 63.821 43.263 62.105 L 43.263 45.817 C 43.752 46.118 44.309 46.275 44.889 46.275 C 45.86 46.275 46.759 45.834 47.353 45.065 C 47.861 44.407 48.082 43.591 47.973 42.767 Z M 45.076 43.309 C 45.016 43.388 44.934 43.401 44.89 43.401 C 44.838 43.401 44.791 43.385 44.749 43.352 L 40.39 39.997 L 40.39 62.107 C 40.39 62.237 40.285 62.342 40.156 62.342 L 29.262 62.342 L 29.263 47.66 L 18.727 47.66 L 18.727 62.342 L 7.635 62.342 C 7.506 62.342 7.402 62.236 7.402 62.107 L 7.402 40.156 L 3.252 43.351 C 3.21 43.384 3.162 43.4 3.11 43.4 C 3.066 43.4 2.984 43.388 2.923 43.309 C 2.874 43.246 2.871 43.18 2.877 43.137 C 2.882 43.093 2.902 43.03 2.965 42.982 L 23.858 26.896 C 23.962 26.817 24.038 26.817 24.144 26.896 L 34.617 34.962 L 34.617 27.081 C 34.617 26.951 34.723 26.847 34.852 26.847 C 34.98 26.847 35.087 26.951 35.087 27.081 L 35.087 35.321 L 45.037 42.982 C 45.1 43.03 45.119 43.092 45.125 43.136 C 45.131 43.182 45.126 43.248 45.076 43.309 Z " />
</g>
<g>
<line
x1="51"
y1="2"
x2="51"
y2="28"
vectorEffect="non-scaling-stroke"
strokeWidth="1"
strokeLinejoin="miter"
strokeLinecap="round"
strokeMiterlimit="3"
/>
<line
x1="64"
y1="15"
x2="38"
y2="15"
vectorEffect="non-scaling-stroke"
strokeWidth="1"
strokeLinejoin="miter"
strokeLinecap="round"
strokeMiterlimit="3"
/>
</g>
</g>
</svg>
</Fragment>
);

View file

@ -1,48 +1,48 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="563.5 323.5 162.599 156.526"
>
<path
d=" M 595 340 L 665 340 C 673.279 340 680 346.721 680 355 L 680 425 C 680 433.279 673.279 440 665 440 L 595 440 C 586.721 440 580 433.279 580 425 L 580 355 C 580 346.721 586.721 340 595 340 Z "
vectorEffect="non-scaling-stroke"
strokeWidth="1"
stroke={color}
strokeLinejoin="miter"
strokeLinecap="square"
strokeMiterlimit="3"
fill="none"
/>
<g>
<path
d=" M 653.959 477.588 L 615.403 363.46 L 726.099 418.249 L 677.594 434.636 L 674.982 436.313 L 653.959 477.588 Z "
fill={'black'}
/>
<path
d=" M 720.556 460.652 L 698.492 480.026 L 638.218 412.176 L 660.996 392.561 L 720.556 460.652 Z "
fill={'black'}
/>
<rect
x="676.273"
y="411.114"
width="15.059"
height="60.236"
transform="matrix(0.75,-0.662,0.662,0.75,-120.833,563.023)"
fill={color}
/>
<path
d=" M 628.319 378.169 L 655.309 458.059 L 669.719 430.144 L 672.331 428.467 L 706.57 416.9 L 628.319 378.169 Z "
fill={color}
/>
</g>
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="563.5 323.5 162.599 156.526"
>
<path
d=" M 595 340 L 665 340 C 673.279 340 680 346.721 680 355 L 680 425 C 680 433.279 673.279 440 665 440 L 595 440 C 586.721 440 580 433.279 580 425 L 580 355 C 580 346.721 586.721 340 595 340 Z "
vectorEffect="non-scaling-stroke"
strokeWidth="1"
stroke={color}
strokeLinejoin="miter"
strokeLinecap="square"
strokeMiterlimit="3"
fill="none"
/>
<g>
<path
d=" M 653.959 477.588 L 615.403 363.46 L 726.099 418.249 L 677.594 434.636 L 674.982 436.313 L 653.959 477.588 Z "
fill={'black'}
/>
<path
d=" M 720.556 460.652 L 698.492 480.026 L 638.218 412.176 L 660.996 392.561 L 720.556 460.652 Z "
fill={'black'}
/>
<rect
x="676.273"
y="411.114"
width="15.059"
height="60.236"
transform="matrix(0.75,-0.662,0.662,0.75,-120.833,563.023)"
fill={color}
/>
<path
d=" M 628.319 378.169 L 655.309 458.059 L 669.719 430.144 L 672.331 428.467 L 706.57 416.9 L 628.319 378.169 Z "
fill={color}
/>
</g>
</svg>
</Fragment>
);

View file

@ -1,22 +1,22 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 32 32"
enableBackground="new 0 0 32 32"
>
<path d="M32 14c0 1.104-0.896 2-2 2H20c-1.104 0-2-0.896-2-2V2c0-1.104 0.896-2 2-2h10c1.104 0 2 0.896 2 2v3c0 0.552-0.447 1-1 1s-1-0.448-1-1V2H20v12h10V9c0-0.552 0.447-1 1-1s1 0.448 1 1V14z" />
<path d="M27 32c-0.553 0-1-0.448-1-1s0.447-1 1-1h3v-8H20v8h3c0.553 0 1 0.448 1 1s-0.447 1-1 1h-3c-1.104 0-2-0.896-2-2v-8c0-1.104 0.896-2 2-2h10c1.104 0 2 0.896 2 2v8c0 1.104-0.896 2-2 2H27z" />
<path d="M0 2c0-1.104 0.896-2 2-2h10c1.104 0 2 0.896 2 2v28c0 1.104-0.896 2-2 2H2c-1.104 0-2-0.896-2-2v-3c0-0.552 0.447-1 1-1s1 0.448 1 1v3h10V2H2v21c0 0.552-0.447 1-1 1s-1-0.448-1-1V2z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 32 32"
enableBackground="new 0 0 32 32"
>
<path d="M32 14c0 1.104-0.896 2-2 2H20c-1.104 0-2-0.896-2-2V2c0-1.104 0.896-2 2-2h10c1.104 0 2 0.896 2 2v3c0 0.552-0.447 1-1 1s-1-0.448-1-1V2H20v12h10V9c0-0.552 0.447-1 1-1s1 0.448 1 1V14z" />
<path d="M27 32c-0.553 0-1-0.448-1-1s0.447-1 1-1h3v-8H20v8h3c0.553 0 1 0.448 1 1s-0.447 1-1 1h-3c-1.104 0-2-0.896-2-2v-8c0-1.104 0.896-2 2-2h10c1.104 0 2 0.896 2 2v8c0 1.104-0.896 2-2 2H27z" />
<path d="M0 2c0-1.104 0.896-2 2-2h10c1.104 0 2 0.896 2 2v28c0 1.104-0.896 2-2 2H2c-1.104 0-2-0.896-2-2v-3c0-0.552 0.447-1 1-1s1 0.448 1 1v3h10V2H2v21c0 0.552-0.447 1-1 1s-1-0.448-1-1V2z" />
</svg>
</Fragment>
);

View file

@ -1,104 +1,104 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{enableBackground: 'new 0 0 480.8 480.8', padding}}
version="1.1"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
viewBox="0 0 480.8 480.8"
xmlSpace="preserve"
>
<path
style={{fill: color}}
d="M317.112,314.4c-22.4,22.4-19.6,67.6-19.6,67.6h-113.6c0,0,2.4-45.2-19.6-67.6
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{enableBackground: 'new 0 0 480.8 480.8', padding}}
version="1.1"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
viewBox="0 0 480.8 480.8"
xmlSpace="preserve"
>
<path
style={{fill: color}}
d="M317.112,314.4c-22.4,22.4-19.6,67.6-19.6,67.6h-113.6c0,0,2.4-45.2-19.6-67.6
c-24.4-21.6-40-52.8-40-87.6c0-64,52-116,116-116s116,52,116,116C356.312,261.6,341.112,292.8,317.112,314.4L317.112,314.4z"
/>
<g>
<path
style={{fill: '#E5E5E5'}}
d="M300.712,417.6c0,6-4.8,10.8-10.8,10.8h-98.8c-6,0-10.8-4.8-10.8-10.8l0,0c0-6,4.8-10.8,10.8-10.8
/>
<g>
<path
style={{fill: '#E5E5E5'}}
d="M300.712,417.6c0,6-4.8,10.8-10.8,10.8h-98.8c-6,0-10.8-4.8-10.8-10.8l0,0c0-6,4.8-10.8,10.8-10.8
h98.4C295.512,406.8,300.712,411.6,300.712,417.6L300.712,417.6z"
/>
<path
style={{fill: '#E5E5E5'}}
d="M285.912,462.4c0,6-4.8,10.8-10.8,10.8h-69.2c-6,0-10.8-4.8-10.8-10.8l0,0c0-6,4.8-10.8,10.8-10.8
/>
<path
style={{fill: '#E5E5E5'}}
d="M285.912,462.4c0,6-4.8,10.8-10.8,10.8h-69.2c-6,0-10.8-4.8-10.8-10.8l0,0c0-6,4.8-10.8,10.8-10.8
h69.2C281.112,451.6,285.912,456.4,285.912,462.4L285.912,462.4z"
/>
</g>
<g>
<path
style={{fill: '#210B20'}}
d="M323.112,318.4c26-23.6,40.8-56.8,40.8-91.6c0-68-55.6-123.6-123.6-123.6s-123.6,55.6-123.6,123.6
/>
</g>
<g>
<path
style={{fill: '#210B20'}}
d="M323.112,318.4c26-23.6,40.8-56.8,40.8-91.6c0-68-55.6-123.6-123.6-123.6s-123.6,55.6-123.6,123.6
c0,35.6,15.6,69.6,42,92.8c19.6,19.6,17.6,61.2,17.6,61.6c0,2,0.8,4,2,5.6c1.6,1.6,3.6,2.4,5.6,2.4h113.2c2,0,4-0.8,5.6-2.4
s2-3.6,2-5.6c0-0.4-2-42,17.6-61.6C322.712,319.2,323.112,318.8,323.112,318.4z M311.912,308.4c-0.8,0.4-1.2,1.2-1.6,2
c-17.6,18.8-20.4,49.6-20.8,64h-98c-0.4-14.8-3.6-46.8-22.4-65.6c-23.6-20.8-37.2-50.4-37.2-81.6c0-60,48.8-108.4,108.4-108.4
c60,0,108.4,48.8,108.4,108.4C348.712,258,335.512,288,311.912,308.4z"
/>
<path
style={{fill: '#210B20'}}
d="M240.312,135.2c-4,0-7.6,3.2-7.6,7.6c0,4,3.2,7.6,7.6,7.6c44.8,0,81.2,36.4,81.2,81.2
/>
<path
style={{fill: '#210B20'}}
d="M240.312,135.2c-4,0-7.6,3.2-7.6,7.6c0,4,3.2,7.6,7.6,7.6c44.8,0,81.2,36.4,81.2,81.2
c0,4,3.2,7.6,7.6,7.6c4,0,7.6-3.2,7.6-7.6C336.712,178.4,293.512,135.2,240.312,135.2z"
/>
<path
style={{fill: '#210B20'}}
d="M308.312,417.6c0-10.4-8.4-18.4-18.4-18.4h-98.8c-10.4,0-18.4,8.4-18.4,18.4
/>
<path
style={{fill: '#210B20'}}
d="M308.312,417.6c0-10.4-8.4-18.4-18.4-18.4h-98.8c-10.4,0-18.4,8.4-18.4,18.4
c0,10.4,8.4,18.4,18.4,18.4h98.4C299.912,436,308.312,428,308.312,417.6z M289.512,420.8h-98.4c-2,0-3.2-1.6-3.2-3.2
c0-2,1.6-3.2,3.2-3.2h98.4c2,0,3.2,1.6,3.2,3.2C293.112,419.6,291.512,420.8,289.512,420.8z"
/>
<path
style={{fill: '#210B20'}}
d="M275.112,444h-69.2c-10.4,0-18.4,8.4-18.4,18.4c0,10.4,8.4,18.4,18.4,18.4h69.2
/>
<path
style={{fill: '#210B20'}}
d="M275.112,444h-69.2c-10.4,0-18.4,8.4-18.4,18.4c0,10.4,8.4,18.4,18.4,18.4h69.2
c10.4,0,18.4-8.4,18.4-18.4C293.512,452.4,285.112,444,275.112,444z M275.112,465.6h-69.2c-2,0-3.2-1.6-3.2-3.2
c0-2,1.6-3.2,3.2-3.2h69.2c2,0,3.2,1.6,3.2,3.2C278.312,464.4,277.112,465.6,275.112,465.6z"
/>
<path
style={{fill: '#210B20'}}
d="M247.912,58.8V7.6c0-4-3.2-7.6-7.6-7.6c-4,0-7.6,3.2-7.6,7.6v51.6c0,4,3.2,7.6,7.6,7.6
/>
<path
style={{fill: '#210B20'}}
d="M247.912,58.8V7.6c0-4-3.2-7.6-7.6-7.6c-4,0-7.6,3.2-7.6,7.6v51.6c0,4,3.2,7.6,7.6,7.6
C244.712,66.4,247.912,63.2,247.912,58.8z"
/>
<path
style={{fill: '#210B20'}}
d="M366.312,38c-3.6-2.4-8-1.2-10.4,2l-28.4,42.8c-2.4,3.6-1.2,8,2,10.4c1.2,0.8,2.8,1.2,4,1.2
/>
<path
style={{fill: '#210B20'}}
d="M366.312,38c-3.6-2.4-8-1.2-10.4,2l-28.4,42.8c-2.4,3.6-1.2,8,2,10.4c1.2,0.8,2.8,1.2,4,1.2
c2.4,0,4.8-1.2,6.4-3.2l28.4-42.8C370.712,45.2,369.512,40.4,366.312,38z"
/>
<path
style={{fill: '#210B20'}}
d="M149.912,92.8c1.2,0,2.8-0.4,4-1.2c3.6-2.4,4.4-6.8,2.4-10.4l-27.6-43.2c-2.4-3.6-6.8-4.4-10.4-2.4
/>
<path
style={{fill: '#210B20'}}
d="M149.912,92.8c1.2,0,2.8-0.4,4-1.2c3.6-2.4,4.4-6.8,2.4-10.4l-27.6-43.2c-2.4-3.6-6.8-4.4-10.4-2.4
c-3.6,2.4-4.4,6.8-2.4,10.4l27.6,43.2C145.112,91.6,147.512,92.8,149.912,92.8z"
/>
<path
style={{fill: '#210B20'}}
d="M43.912,128.8l45.2,24.4c1.2,0.8,2.4,0.8,3.6,0.8c2.8,0,5.2-1.6,6.8-4c2-3.6,0.8-8.4-3.2-10.4
/>
<path
style={{fill: '#210B20'}}
d="M43.912,128.8l45.2,24.4c1.2,0.8,2.4,0.8,3.6,0.8c2.8,0,5.2-1.6,6.8-4c2-3.6,0.8-8.4-3.2-10.4
l-45.2-24.4c-3.6-2-8.4-0.8-10.4,3.2C39.112,122.4,40.312,126.8,43.912,128.8z"
/>
<path
style={{fill: '#210B20'}}
d="M387.912,154.4c1.2,0,2.4-0.4,3.6-0.8l45.2-24.4c3.6-2,5.2-6.4,3.2-10.4c-2-3.6-6.4-5.2-10.4-3.2
/>
<path
style={{fill: '#210B20'}}
d="M387.912,154.4c1.2,0,2.4-0.4,3.6-0.8l45.2-24.4c3.6-2,5.2-6.4,3.2-10.4c-2-3.6-6.4-5.2-10.4-3.2
l-45.2,24.4c-3.6,2-5.2,6.4-3.2,10.4C382.312,152.8,385.112,154.4,387.912,154.4z"
/>
</g>
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
</svg>
</Fragment>
/>
</g>
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
<g />
</svg>
</Fragment>
);

View file

@ -3,5 +3,5 @@ import React from 'react';
import logoImage from '../../../resources/logo.svg';
export default ({width, height, color, padding, margin}) => (
<img src={logoImage} height={height} width={width} alt="" />
<img src={logoImage} height={height} width={width} alt="" />
);

View file

@ -1,16 +1,16 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M19,2H5A3,3,0,0,0,2,5V19a3,3,0,0,0,3,3H19a2.81,2.81,0,0,0,.49-.05l.3-.07.07,0h0l.05,0,.37-.14.13-.07c.1-.06.21-.11.31-.18a3.79,3.79,0,0,0,.38-.32l.07-.09a2.69,2.69,0,0,0,.27-.32l.09-.13a2.31,2.31,0,0,0,.18-.35,1,1,0,0,0,.07-.15c.05-.12.08-.25.12-.38l0-.15A2.6,2.6,0,0,0,22,19V5A3,3,0,0,0,19,2ZM5,20a1,1,0,0,1-1-1V14.69l3.29-3.3h0a1,1,0,0,1,1.42,0L17.31,20Zm15-1a1,1,0,0,1-.07.36,1,1,0,0,1-.08.14.94.94,0,0,1-.09.12l-5.35-5.35.88-.88a1,1,0,0,1,1.42,0h0L20,16.69Zm0-5.14L18.12,12a3.08,3.08,0,0,0-4.24,0l-.88.88L10.12,10a3.08,3.08,0,0,0-4.24,0L4,11.86V5A1,1,0,0,1,5,4H19a1,1,0,0,1,1,1ZM13.5,6A1.5,1.5,0,1,0,15,7.5,1.5,1.5,0,0,0,13.5,6Z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M19,2H5A3,3,0,0,0,2,5V19a3,3,0,0,0,3,3H19a2.81,2.81,0,0,0,.49-.05l.3-.07.07,0h0l.05,0,.37-.14.13-.07c.1-.06.21-.11.31-.18a3.79,3.79,0,0,0,.38-.32l.07-.09a2.69,2.69,0,0,0,.27-.32l.09-.13a2.31,2.31,0,0,0,.18-.35,1,1,0,0,0,.07-.15c.05-.12.08-.25.12-.38l0-.15A2.6,2.6,0,0,0,22,19V5A3,3,0,0,0,19,2ZM5,20a1,1,0,0,1-1-1V14.69l3.29-3.3h0a1,1,0,0,1,1.42,0L17.31,20Zm15-1a1,1,0,0,1-.07.36,1,1,0,0,1-.08.14.94.94,0,0,1-.09.12l-5.35-5.35.88-.88a1,1,0,0,1,1.42,0h0L20,16.69Zm0-5.14L18.12,12a3.08,3.08,0,0,0-4.24,0l-.88.88L10.12,10a3.08,3.08,0,0,0-4.24,0L4,11.86V5A1,1,0,0,1,5,4H19a1,1,0,0,1,1,1ZM13.5,6A1.5,1.5,0,1,0,15,7.5,1.5,1.5,0,0,0,13.5,6Z" />
</svg>
</Fragment>
);

View file

@ -1,16 +1,16 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M21,11a1,1,0,0,0-1,1,8.05,8.05,0,1,1-2.22-5.5h-2.4a1,1,0,0,0,0,2h4.53a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4.77A10,10,0,1,0,22,12,1,1,0,0,0,21,11Z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M21,11a1,1,0,0,0-1,1,8.05,8.05,0,1,1-2.22-5.5h-2.4a1,1,0,0,0,0,2h4.53a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4.77A10,10,0,1,0,22,12,1,1,0,0,0,21,11Z" />
</svg>
</Fragment>
);

View file

@ -1,8 +1,8 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
{/*<svg
<Fragment>
{/*<svg
width={width}
height={height}
fill={color}
@ -12,29 +12,29 @@ export default ({width, height, color, padding, margin}) => (
>
<path d="M20,10.5a1,1,0,0,0-1,1v7a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1v-8a1,1,0,0,1,1-1H6a1,1,0,0,0,1-.68l.54-1.64a1,1,0,0,1,.95-.68H14a1,1,0,0,0,0-2H8.44A3,3,0,0,0,5.6,6.55l-.32,1H4a3,3,0,0,0-3,3v8a3,3,0,0,0,3,3H18a3,3,0,0,0,3-3v-7A1,1,0,0,0,20,10.5Zm-9-1a4,4,0,1,0,4,4A4,4,0,0,0,11,9.5Zm0,6a2,2,0,1,1,2-2A2,2,0,0,1,11,15.5Zm11-11H21v-1a1,1,0,0,0-2,0v1H18a1,1,0,0,0,0,2h1v1a1,1,0,0,0,2,0v-1h1a1,1,0,0,0,0-2Z" />
</svg>*/}
<svg
width={width}
height={height}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 100"
>
<g transform="translate(0,-952.36218)">
<path
d="m 83,958.36218 c -1.1046,0 -2,0.89543 -2,2 l 0,7 -7,0 c -1.10457,0 -2,0.8954 -2,2 0,1.1046 0.89543,2 2,2 l 7,0 0,7 c 0,1.10457 0.8954,2 2,2 1.1046,0 2,-0.89543 2,-2 l 0,-7 7,0 c 1.10457,0 2,-0.8954 2,-2 0,-1.1046 -0.89543,-2 -2,-2 l -7,0 0,-7 c 0,-1.10457 -0.8954,-2 -2,-2 z m -48,22 c -0.78068,0.007 -1.3909,0.40265 -1.71875,0.96875 l -6.4375,11.03125 -12.84375,0 c -4.3973999,0 -8,3.6026 -8,8.00002 l 0,38 c 0,4.3974 3.6026001,8 8,8 l 62,0 c 4.3974,0 8,-3.6026 8,-8 l 0,-38 c 0,-4.39742 -3.6026,-8.00002 -8,-8.00002 l -12.84375,0 -6.4375,-11.03125 C 56.3641,980.74168 55.68774,980.36046 55,980.36218 l -20,0 z m 1.125,4 17.75,0 6.40625,11 c 0.34687,0.60075 1.02507,0.99534 1.71875,1 l 14,0 c 2.25056,0 4,1.74944 4,4.00002 l 0,38 c 0,2.2505 -1.74944,4 -4,4 l -62,0 c -2.25056,0 -4,-1.7495 -4,-4 l 0,-38 c 0,-2.25058 1.74944,-4.00002 4,-4.00002 l 14,0 c 0.69368,-0.005 1.37188,-0.39925 1.71875,-1 l 6.40625,-11 z M 45,1002.3622 c -8.81287,0 -16,7.1871 -16,16 0,8.8128 7.18713,16 16,16 8.81286,0 16,-7.1872 16,-16 0,-8.8129 -7.18714,-16 -16,-16 z m 0,4 c 6.6511,0 12,5.3489 12,12 0,6.6511 -5.3489,12 -12,12 -6.65111,0 -12,-5.3489 -12,-12 0,-6.6511 5.34889,-12 12,-12 z"
fill={color}
fillOpacity="1"
stroke="none"
marker="none"
visibility="visible"
display="inline"
overflow="visible"
/>
</g>
</svg>
</Fragment>
<svg
width={width}
height={height}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 100"
>
<g transform="translate(0,-952.36218)">
<path
d="m 83,958.36218 c -1.1046,0 -2,0.89543 -2,2 l 0,7 -7,0 c -1.10457,0 -2,0.8954 -2,2 0,1.1046 0.89543,2 2,2 l 7,0 0,7 c 0,1.10457 0.8954,2 2,2 1.1046,0 2,-0.89543 2,-2 l 0,-7 7,0 c 1.10457,0 2,-0.8954 2,-2 0,-1.1046 -0.89543,-2 -2,-2 l -7,0 0,-7 c 0,-1.10457 -0.8954,-2 -2,-2 z m -48,22 c -0.78068,0.007 -1.3909,0.40265 -1.71875,0.96875 l -6.4375,11.03125 -12.84375,0 c -4.3973999,0 -8,3.6026 -8,8.00002 l 0,38 c 0,4.3974 3.6026001,8 8,8 l 62,0 c 4.3974,0 8,-3.6026 8,-8 l 0,-38 c 0,-4.39742 -3.6026,-8.00002 -8,-8.00002 l -12.84375,0 -6.4375,-11.03125 C 56.3641,980.74168 55.68774,980.36046 55,980.36218 l -20,0 z m 1.125,4 17.75,0 6.40625,11 c 0.34687,0.60075 1.02507,0.99534 1.71875,1 l 14,0 c 2.25056,0 4,1.74944 4,4.00002 l 0,38 c 0,2.2505 -1.74944,4 -4,4 l -62,0 c -2.25056,0 -4,-1.7495 -4,-4 l 0,-38 c 0,-2.25058 1.74944,-4.00002 4,-4.00002 l 14,0 c 0.69368,-0.005 1.37188,-0.39925 1.71875,-1 l 6.40625,-11 z M 45,1002.3622 c -8.81287,0 -16,7.1871 -16,16 0,8.8128 7.18713,16 16,16 8.81286,0 16,-7.1872 16,-16 0,-8.8129 -7.18714,-16 -16,-16 z m 0,4 c 6.6511,0 12,5.3489 12,12 0,6.6511 -5.3489,12 -12,12 -6.65111,0 -12,-5.3489 -12,-12 0,-6.6511 5.34889,-12 12,-12 z"
fill={color}
fillOpacity="1"
stroke="none"
marker="none"
visibility="visible"
display="inline"
overflow="visible"
/>
</g>
</svg>
</Fragment>
);

View file

@ -1,27 +1,27 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100"
version="1.1"
x="0px"
y="0px"
>
<g stroke="none" strokeWidth="1" fillRule="evenodd">
<g>
<path d="M32,23.004355 L32,23.004355 L32,48.995645 C32,58.9442689 40.056121,67 50,67 C59.942622,67 68,58.9403509 68,48.995645 L68,23.004355 C68,13.0557311 59.943879,5 50,5 C40.057378,5 32,13.0596491 32,23.004355 L32,23.004355 Z M28,23.004355 C28,10.8516853 37.847064,1 50,1 C62.1502645,1 72,10.8438387 72,23.004355 L72,48.995645 C72,61.1483147 62.152936,71 50,71 C37.8497355,71 28,61.1561613 28,48.995645 L28,23.004355 L28,23.004355 Z" />
<path d="M48,17.0085302 C48,15.8992496 48.8877296,15 50,15 C51.1045695,15 52,15.9019504 52,17.0085302 L52,24.9914698 C52,26.1007504 51.1122704,27 50,27 C48.8954305,27 48,26.0980496 48,24.9914698 L48,17.0085302 Z" />
<path d="M50,95.1715729 L58.5857864,86.5857864 C59.366835,85.8047379 60.633165,85.8047379 61.4142136,86.5857864 C62.1952621,87.366835 62.1952621,88.633165 61.4142136,89.4142136 L51.4142136,99.4142136 C51.0236893,99.8047379 50.5118446,100 50,100 C49.4881554,100 48.9763107,99.8047379 48.5857864,99.4142136 L38.5857864,89.4142136 C37.8047379,88.633165 37.8047379,87.366835 38.5857864,86.5857864 C39.366835,85.8047379 40.633165,85.8047379 41.4142136,86.5857864 L50,95.1715729 Z" />
<path d="M50,82.1715729 L58.5857864,73.5857864 C59.366835,72.8047379 60.633165,72.8047379 61.4142136,73.5857864 C62.1952621,74.366835 62.1952621,75.633165 61.4142136,76.4142136 L51.4142136,86.4142136 C51.0236893,86.8047379 50.5118446,87 50,87 C49.4881554,87 48.9763107,86.8047379 48.5857864,86.4142136 L38.5857864,76.4142136 C37.8047379,75.633165 37.8047379,74.366835 38.5857864,73.5857864 C39.366835,72.8047379 40.633165,72.8047379 41.4142136,73.5857864 L50,82.1715729 Z" />
</g>
</g>
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100"
version="1.1"
x="0px"
y="0px"
>
<g stroke="none" strokeWidth="1" fillRule="evenodd">
<g>
<path d="M32,23.004355 L32,23.004355 L32,48.995645 C32,58.9442689 40.056121,67 50,67 C59.942622,67 68,58.9403509 68,48.995645 L68,23.004355 C68,13.0557311 59.943879,5 50,5 C40.057378,5 32,13.0596491 32,23.004355 L32,23.004355 Z M28,23.004355 C28,10.8516853 37.847064,1 50,1 C62.1502645,1 72,10.8438387 72,23.004355 L72,48.995645 C72,61.1483147 62.152936,71 50,71 C37.8497355,71 28,61.1561613 28,48.995645 L28,23.004355 L28,23.004355 Z" />
<path d="M48,17.0085302 C48,15.8992496 48.8877296,15 50,15 C51.1045695,15 52,15.9019504 52,17.0085302 L52,24.9914698 C52,26.1007504 51.1122704,27 50,27 C48.8954305,27 48,26.0980496 48,24.9914698 L48,17.0085302 Z" />
<path d="M50,95.1715729 L58.5857864,86.5857864 C59.366835,85.8047379 60.633165,85.8047379 61.4142136,86.5857864 C62.1952621,87.366835 62.1952621,88.633165 61.4142136,89.4142136 L51.4142136,99.4142136 C51.0236893,99.8047379 50.5118446,100 50,100 C49.4881554,100 48.9763107,99.8047379 48.5857864,99.4142136 L38.5857864,89.4142136 C37.8047379,88.633165 37.8047379,87.366835 38.5857864,86.5857864 C39.366835,85.8047379 40.633165,85.8047379 41.4142136,86.5857864 L50,95.1715729 Z" />
<path d="M50,82.1715729 L58.5857864,73.5857864 C59.366835,72.8047379 60.633165,72.8047379 61.4142136,73.5857864 C62.1952621,74.366835 62.1952621,75.633165 61.4142136,76.4142136 L51.4142136,86.4142136 C51.0236893,86.8047379 50.5118446,87 50,87 C49.4881554,87 48.9763107,86.8047379 48.5857864,86.4142136 L38.5857864,76.4142136 C37.8047379,75.633165 37.8047379,74.366835 38.5857864,73.5857864 C39.366835,72.8047379 40.633165,72.8047379 41.4142136,73.5857864 L50,82.1715729 Z" />
</g>
</g>
</svg>
</Fragment>
);

View file

@ -1,27 +1,27 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100"
version="1.1"
x="0px"
y="0px"
>
<g stroke="none" strokeWidth="1" fillRule="evenodd">
<g>
<path d="M42,21c0.5,0,1-0.2,1.4-0.6l6.6-6.6l6.6,6.6C57,20.8,57.5,21,58,21s1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8l-8-8 c-0.8-0.8-2-0.8-2.8,0l-8,8c-0.8,0.8-0.8,2,0,2.8C41,20.8,41.5,21,42,21z" />
<path d="M40.6,31.4C41,31.8,41.5,32,42,32s1-0.2,1.4-0.6l6.6-6.6l6.6,6.6C57,31.8,57.5,32,58,32s1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8 l-8-8c-0.8-0.8-2-0.8-2.8,0l-8,8C39.8,29.4,39.8,30.6,40.6,31.4z" />
<path d="M50,37c-9.9,0-18,8.1-18,18v18c0,9.9,8.1,18,18,18s18-8.1,18-18V55C68,45.1,59.9,37,50,37z M64,73c0,7.7-6.3,14-14,14 s-14-6.3-14-14V55c0-7.7,6.3-14,14-14s14,6.3,14,14V73z" />
<path d="M50,48c-1.1,0-2,0.9-2,2v6c0,1.1,0.9,2,2,2s2-0.9,2-2v-6C52,48.9,51.1,48,50,48z" />
</g>
</g>
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100"
version="1.1"
x="0px"
y="0px"
>
<g stroke="none" strokeWidth="1" fillRule="evenodd">
<g>
<path d="M42,21c0.5,0,1-0.2,1.4-0.6l6.6-6.6l6.6,6.6C57,20.8,57.5,21,58,21s1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8l-8-8 c-0.8-0.8-2-0.8-2.8,0l-8,8c-0.8,0.8-0.8,2,0,2.8C41,20.8,41.5,21,42,21z" />
<path d="M40.6,31.4C41,31.8,41.5,32,42,32s1-0.2,1.4-0.6l6.6-6.6l6.6,6.6C57,31.8,57.5,32,58,32s1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8 l-8-8c-0.8-0.8-2-0.8-2.8,0l-8,8C39.8,29.4,39.8,30.6,40.6,31.4z" />
<path d="M50,37c-9.9,0-18,8.1-18,18v18c0,9.9,8.1,18,18,18s18-8.1,18-18V55C68,45.1,59.9,37,50,37z M64,73c0,7.7-6.3,14-14,14 s-14-6.3-14-14V55c0-7.7,6.3-14,14-14s14,6.3,14,14V73z" />
<path d="M50,48c-1.1,0-2,0.9-2,2v6c0,1.1,0.9,2,2,2s2-0.9,2-2v-6C52,48.9,51.1,48,50,48z" />
</g>
</g>
</svg>
</Fragment>
);

View file

@ -2,33 +2,37 @@ import React, {Fragment} from 'react';
import styles from './styles.css';
export default ({width, height, color = '#ffffff80', padding}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
className={styles.iconCheck}
viewBox="0 0 225 225"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
xmlSpace="preserve"
>
<g transform="matrix(1,0,0,1,-178.667,-170.667)">
<g className={styles.check} transform="matrix(1,0,0,1,176,113)">
<path
d="M65,166L101,202L165,138"
style={{fill: 'none', stroke: color, strokeWidth: 16.67}}
/>
</g>
<circle
className={styles.circle}
cx="291"
cy="283"
r="104"
style={{stroke: color}}
/>
</g>
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
className={styles.iconCheck}
viewBox="0 0 225 225"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
xmlSpace="preserve"
>
<g transform="matrix(1,0,0,1,-178.667,-170.667)">
<g className={styles.check} transform="matrix(1,0,0,1,176,113)">
<path
d="M65,166L101,202L165,138"
style={{
fill: 'none',
stroke: color,
strokeWidth: 16.67,
}}
/>
</g>
<circle
className={styles.circle}
cx="291"
cy="283"
r="104"
style={{stroke: color}}
/>
</g>
</svg>
</Fragment>
);

View file

@ -1,27 +1,27 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 100"
enableBackground="new 0 0 100 100"
xmlSpace="preserve"
>
<g>
<g>
<path d="M79.859,20.143c-0.778-0.777-2.037-0.777-2.815,0l-9.222,9.223c-1.901-1.334-4.156-2.061-6.527-2.061 c-3.046,0-5.909,1.186-8.061,3.34l-5.621,5.619c-0.374,0.373-0.583,0.879-0.583,1.406s0.209,1.035,0.583,1.408l13.308,13.309 c0.39,0.389,0.898,0.582,1.408,0.582c0.509,0,1.019-0.193,1.407-0.582l5.621-5.619c3.962-3.965,4.38-10.141,1.275-14.584 l9.227-9.227C80.637,22.18,80.637,20.92,79.859,20.143z M66.543,43.953l-4.214,4.211L51.835,37.67l4.215-4.211 c1.4-1.402,3.264-2.174,5.245-2.174c1.982,0,3.846,0.773,5.248,2.174C69.434,36.354,69.434,41.059,66.543,43.953z" />
<path d="M51.213,52.229l-4.352,4.352l-3.441-3.44l4.351-4.351c0.778-0.777,0.778-2.037,0-2.814c-0.777-0.777-2.036-0.777-2.814,0 l-4.351,4.351l-2.12-2.12c-0.777-0.777-2.037-0.777-2.814,0l-5.619,5.619c-3.964,3.965-4.383,10.141-1.276,14.585l-8.635,8.634 c-0.777,0.777-0.777,2.037,0,2.814c0.389,0.389,0.898,0.584,1.407,0.584c0.51,0,1.019-0.195,1.408-0.584l8.632-8.631 c1.9,1.334,4.154,2.061,6.525,2.061c3.045,0,5.907-1.186,8.062-3.34l5.619-5.619c0.777-0.777,0.777-2.037,0-2.814l-2.118-2.119 l4.352-4.352c0.777-0.777,0.777-2.037,0-2.814S51.99,51.451,51.213,52.229z M43.36,67.133c-1.402,1.4-3.266,2.174-5.247,2.174 s-3.845-0.773-5.247-2.174c-2.893-2.893-2.893-7.6,0-10.494l4.211-4.211l10.494,10.494L43.36,67.133z" />
</g>
</g>
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 100"
enableBackground="new 0 0 100 100"
xmlSpace="preserve"
>
<g>
<g>
<path d="M79.859,20.143c-0.778-0.777-2.037-0.777-2.815,0l-9.222,9.223c-1.901-1.334-4.156-2.061-6.527-2.061 c-3.046,0-5.909,1.186-8.061,3.34l-5.621,5.619c-0.374,0.373-0.583,0.879-0.583,1.406s0.209,1.035,0.583,1.408l13.308,13.309 c0.39,0.389,0.898,0.582,1.408,0.582c0.509,0,1.019-0.193,1.407-0.582l5.621-5.619c3.962-3.965,4.38-10.141,1.275-14.584 l9.227-9.227C80.637,22.18,80.637,20.92,79.859,20.143z M66.543,43.953l-4.214,4.211L51.835,37.67l4.215-4.211 c1.4-1.402,3.264-2.174,5.245-2.174c1.982,0,3.846,0.773,5.248,2.174C69.434,36.354,69.434,41.059,66.543,43.953z" />
<path d="M51.213,52.229l-4.352,4.352l-3.441-3.44l4.351-4.351c0.778-0.777,0.778-2.037,0-2.814c-0.777-0.777-2.036-0.777-2.814,0 l-4.351,4.351l-2.12-2.12c-0.777-0.777-2.037-0.777-2.814,0l-5.619,5.619c-3.964,3.965-4.383,10.141-1.276,14.585l-8.635,8.634 c-0.777,0.777-0.777,2.037,0,2.814c0.389,0.389,0.898,0.584,1.407,0.584c0.51,0,1.019-0.195,1.408-0.584l8.632-8.631 c1.9,1.334,4.154,2.061,6.525,2.061c3.045,0,5.907-1.186,8.062-3.34l5.619-5.619c0.777-0.777,0.777-2.037,0-2.814l-2.118-2.119 l4.352-4.352c0.777-0.777,0.777-2.037,0-2.814S51.99,51.451,51.213,52.229z M43.36,67.133c-1.402,1.4-3.266,2.174-5.247,2.174 s-3.845-0.773-5.247-2.174c-2.893-2.893-2.893-7.6,0-10.494l4.211-4.211l10.494,10.494L43.36,67.133z" />
</g>
</g>
</svg>
</Fragment>
);

View file

@ -1,16 +1,16 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 88 88"
>
<path d="m0 12.402 35.687-4.8602.0156 34.423-35.67.20313zm35.67 33.529.0277 34.453-35.67-4.9041-.002-29.78zm4.3261-39.025 47.318-6.906v41.527l-47.318.37565zm47.329 39.349-.0111 41.34-47.318-6.6784-.0663-34.739z" />
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 88 88"
>
<path d="m0 12.402 35.687-4.8602.0156 34.423-35.67.20313zm35.67 33.529.0277 34.453-35.67-4.9041-.002-29.78zm4.3261-39.025 47.318-6.906v41.527l-47.318.37565zm47.329 39.349-.0111 41.34-47.318-6.6784-.0663-34.739z" />
</svg>
</Fragment>
);

View file

@ -1,24 +1,24 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 100"
enableBackground="new 0 0 100 100"
>
<g>
<path d="M91.414,88.586L70.815,67.987C76.522,61.614,80,53.207,80,44C80,24.149,63.851,8,44,8S8,24.149,8,44 s16.149,36,36,36c9.207,0,17.614-3.478,23.987-9.185l20.599,20.599C88.977,91.805,89.488,92,90,92s1.023-0.195,1.414-0.586 C92.195,90.633,92.195,89.367,91.414,88.586z M44,76c-17.645,0-32-14.355-32-32s14.355-32,32-32s32,14.355,32,32S61.645,76,44,76z" />
<path d="M62,42H46V26c0-1.104-0.896-2-2-2s-2,0.896-2,2v16H26c-1.104,0-2,0.896-2,2s0.896,2,2,2h16v16 c0,1.104,0.896,2,2,2s2-0.896,2-2V46h16c1.104,0,2-0.896,2-2S63.104,42,62,42z" />
</g>
</svg>
</Fragment>
<Fragment>
<svg
height={height}
width={width}
fill={color}
style={{padding, margin}}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 100"
enableBackground="new 0 0 100 100"
>
<g>
<path d="M91.414,88.586L70.815,67.987C76.522,61.614,80,53.207,80,44C80,24.149,63.851,8,44,8S8,24.149,8,44 s16.149,36,36,36c9.207,0,17.614-3.478,23.987-9.185l20.599,20.599C88.977,91.805,89.488,92,90,92s1.023-0.195,1.414-0.586 C92.195,90.633,92.195,89.367,91.414,88.586z M44,76c-17.645,0-32-14.355-32-32s14.355-32,32-32s32,14.355,32,32S61.645,76,44,76z" />
<path d="M62,42H46V26c0-1.104-0.896-2-2-2s-2,0.896-2,2v16H26c-1.104,0-2,0.896-2,2s0.896,2,2,2h16v16 c0,1.104,0.896,2,2,2s2-0.896,2-2V46h16c1.104,0,2-0.896,2-2S63.104,42,62,42z" />
</g>
</svg>
</Fragment>
);

File diff suppressed because it is too large Load diff

View file

@ -2,21 +2,21 @@
import chromeEmulatedDevices from './chromeEmulatedDevices';
export const OS: {[key: string]: OS} = {
ios: 'iOS',
android: 'Android',
windowsPhone: 'Windows Phone',
pc: 'PC',
ios: 'iOS',
android: 'Android',
windowsPhone: 'Windows Phone',
pc: 'PC',
};
export const DEVICE_TYPE: {[key: string]: DeviceType} = {
phone: 'phone',
tablet: 'tablet',
desktop: 'notebook',
phone: 'phone',
tablet: 'tablet',
desktop: 'notebook',
};
export const CAPABILITIES: {[key: string]: Capability} = {
mobile: 'mobile',
touch: 'touch',
mobile: 'mobile',
touch: 'touch',
};
type OSType = OS.ios | OS.android | OS.windowsPhone | OS.pc;
@ -26,49 +26,49 @@ type DeviceType = DEVICE_TYPE.phone | DEVICE_TYPE.tablet | DEVICE_TYPE.desktop;
type Capability = CAPABILITIES.mobile | CAPABILITIES.touch;
export type Device = {
id: number,
added: boolean,
width: number,
height: number,
name: string,
useragent: string,
capabilities: Array<Capability>,
os: OSType,
type: DeviceType,
id: number,
added: boolean,
width: number,
height: number,
name: string,
useragent: string,
capabilities: Array<Capability>,
os: OSType,
type: DeviceType,
};
function getOS(device) {
if (device.capabilities.indexOf('mobile') > -1) {
const useragent = device['user-agent'];
if (useragent.indexOf('like Mac OS X') > -1) {
return OS.ios;
if (device.capabilities.indexOf('mobile') > -1) {
const useragent = device['user-agent'];
if (useragent.indexOf('like Mac OS X') > -1) {
return OS.ios;
}
if (useragent.indexOf('Lumia') > -1) {
return OS.windowsPhone;
}
return OS.android;
}
if (useragent.indexOf('Lumia') > -1) {
return OS.windowsPhone;
}
return OS.android;
}
return OS.pc;
return OS.pc;
}
let idGen = 1;
export default chromeEmulatedDevices.extensions
.sort((a, b) => a.order - b.order)
.map(({order, device}) => {
const dimension =
device.type === DEVICE_TYPE.desktop
? device.screen.horizontal
: device.screen.vertical;
.sort((a, b) => a.order - b.order)
.map(({order, device}) => {
const dimension =
device.type === DEVICE_TYPE.desktop
? device.screen.horizontal
: device.screen.vertical;
return {
id: idGen++,
name: device.title,
width: dimension.width,
height: dimension.height,
useragent: device['user-agent'],
capabilities: device.capabilities,
added: device['show-by-default'],
os: getOS(device),
type: device.type,
};
});
return {
id: idGen++,
name: device.title,
width: dimension.width,
height: dimension.height,
useragent: device['user-agent'],
capabilities: device.capabilities,
added: device['show-by-default'],
os: getOS(device),
type: device.type,
};
});

View file

@ -1,3 +1,3 @@
export const DEACTIVATION_REASON = {
REVALIDATION: 'REVALIDATION',
REVALIDATION: 'REVALIDATION',
};

View file

@ -1,4 +1,4 @@
export default {
HOME: '/',
COUNTER: '/counter',
HOME: '/',
COUNTER: '/counter',
};

View file

@ -7,16 +7,16 @@ import AddDevice from '../../components/DeviceManager/AddDevice';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(AddDevice);

View file

@ -7,27 +7,27 @@ import AddressInput from '../../components/Addressinput';
import * as BrowserActions from '../../actions/browser';
const AddressBar = function(props) {
return (
<AddressInput
address={props.browser.address}
onChange={props.onAddressChange}
homepage={props.browser.homepage}
setHomepage={props.setCurrentAddressAsHomepage}
/>
);
return (
<AddressInput
address={props.browser.address}
onChange={props.onAddressChange}
homepage={props.browser.homepage}
setHomepage={props.setCurrentAddressAsHomepage}
/>
);
};
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(AddressBar);

View file

@ -2,14 +2,14 @@
import * as React from 'react';
type Props = {
children: React.Node
children: React.Node,
};
export default class App extends React.Component<Props> {
props: Props;
props: Props;
render() {
const { children } = this.props;
return <React.Fragment>{children}</React.Fragment>;
}
render() {
const {children} = this.props;
return <React.Fragment>{children}</React.Fragment>;
}
}

View file

@ -11,32 +11,38 @@ import Grid from '@material-ui/core/Grid';
type Props = {};
class Browser extends React.Component<Props> {
props: Props;
props: Props;
render() {
return (
<Fragment>
<Header />
<div style={{display: 'flex', flexDirection: 'row', height: '100%'}}>
<DrawerContainer />
<DevicePreviewerContainer />
</div>
</Fragment>
);
}
render() {
return (
<Fragment>
<Header />
<div
style={{
display: 'flex',
flexDirection: 'row',
height: '100%',
}}
>
<DrawerContainer />
<DevicePreviewerContainer />
</div>
</Fragment>
);
}
}
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(Browser);

View file

@ -7,16 +7,16 @@ import DeviceDrawer from '../../components/DeviceDrawer';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(DeviceDrawer);

View file

@ -7,16 +7,16 @@ import DeviceManager from '../../components/DeviceManager';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(DeviceManager);

View file

@ -6,16 +6,16 @@ import DevicesPreviewer from '../../components/DevicesPreviewer';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(DevicesPreviewer);

View file

@ -7,16 +7,16 @@ import DevicesOverview from '../../components/DevicesOverview';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(DevicesOverview);

View file

@ -7,16 +7,16 @@ import Drawer from '../../components/Drawer';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
drawer: state.browser.drawer,
};
return {
drawer: state.browser.drawer,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(Drawer);

View file

@ -1,13 +1,13 @@
// @flow
import React, { Component } from 'react';
import React, {Component} from 'react';
import Home from '../components/Home';
type Props = {};
export default class HomePage extends Component<Props> {
props: Props;
props: Props;
render() {
return <Home />;
}
render() {
return <Home />;
}
}

View file

@ -7,16 +7,16 @@ import LeftIconsPane from '../../components/LeftIconsPane';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
drawer: state.browser.drawer,
};
return {
drawer: state.browser.drawer,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(LeftIconsPane);

View file

@ -7,17 +7,17 @@ import * as BrowserActions from '../../actions/browser';
import NavigationControls from '../../components/NavigationControls';
function mapStateToProps(state) {
const {
navigatorStatus: {backEnabled, forwardEnabled},
} = state.browser;
return {backEnabled, forwardEnabled};
const {
navigatorStatus: {backEnabled, forwardEnabled},
} = state.browser;
return {backEnabled, forwardEnabled};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(NavigationControls);

View file

@ -7,16 +7,16 @@ import QuickFilterDevices from '../../components/QuickFilterDevices';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(QuickFilterDevices);

View file

@ -12,53 +12,53 @@ import {themeColor} from '../constants/colors';
import ErrorBoundary from '../components/ErrorBoundary';
type Props = {
store: Store,
history: {},
store: Store,
history: {},
};
const theme = createMuiTheme({
palette: {
type: 'dark',
primary: {
main: themeColor,
palette: {
type: 'dark',
primary: {
main: themeColor,
},
secondary: {
main: '#424242',
},
ternary: {
main: '#C4C5CE',
},
divider: grey[500],
background: {
main: '#252526',
},
},
secondary: {
main: '#424242',
},
ternary: {
main: '#C4C5CE',
},
divider: grey[500],
background: {
main: '#252526',
},
},
});
const getApp = history => {
if (true || process.env.NODE_ENV !== 'development') {
if (true || process.env.NODE_ENV !== 'development') {
return (
<ErrorBoundary>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</ErrorBoundary>
);
}
return (
<ErrorBoundary>
<ConnectedRouter history={history}>
<Routes />
<Routes />
</ConnectedRouter>
</ErrorBoundary>
);
}
return (
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
);
};
export default class Root extends Component<Props> {
render() {
const {store, history} = this.props;
return (
<Provider store={store}>
<ThemeProvider theme={theme}>{getApp(history)}</ThemeProvider>
</Provider>
);
}
render() {
const {store, history} = this.props;
return (
<Provider store={store}>
<ThemeProvider theme={theme}>{getApp(history)}</ThemeProvider>
</Provider>
);
}
}

View file

@ -7,16 +7,16 @@ import ScrollControls from '../../components/ScrollControls';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(ScrollControls);

View file

@ -6,16 +6,16 @@ import WebView from '../../components/WebView';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(WebView);

View file

@ -7,16 +7,16 @@ import ZoomInput from '../../components/ZoomInput';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
browser: state.browser,
};
return {
browser: state.browser,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(ZoomInput);

View file

@ -10,52 +10,55 @@ const fs = require('fs-extra');
const tempDir = path.join(os.tmpdir(), UUID());
registerPromiseWorker(({images, direction, resultFilename}) => {
if (direction === 'horizontal') {
return stitchHorizontally(images);
}
return stitchVertically(images, resultFilename);
if (direction === 'horizontal') {
return stitchHorizontally(images);
}
return stitchVertically(images, resultFilename);
});
async function stitchHorizontally(images) {
const result = await mergeImg(images.map(img => ({src: Buffer.from(img)})), {
direction: false,
});
const tempPath = await writeToTempFile(result);
return tempPath;
const result = await mergeImg(
images.map(img => ({src: Buffer.from(img)})),
{
direction: false,
}
);
const tempPath = await writeToTempFile(result);
return tempPath;
}
async function writeToTempFile(image) {
return new Promise(async (resolve, reject) => {
await fs.ensureDir(tempDir);
const tempPath = path.join(tempDir, `${UUID()}.png`);
await image.write(tempPath, err => {
if (err) {
return reject(err);
}
return resolve(tempPath);
return new Promise(async (resolve, reject) => {
await fs.ensureDir(tempDir);
const tempPath = path.join(tempDir, `${UUID()}.png`);
await image.write(tempPath, err => {
if (err) {
return reject(err);
}
return resolve(tempPath);
});
});
});
}
async function stitchVertically(images, {dir, file}) {
const result = (await mergeImg(
await Promise.all(
images.map(async img => {
const JimpImg = await Jimp.read(img);
return {
src: await JimpImg.getBufferAsync('image/png'),
};
})
),
{
direction: true,
}
))
.rgba(false)
.background(0xffffffff);
await fs.ensureDir(dir);
await Promise.all([
result.write(path.join(dir, file)),
...images.map(image => fs.remove(image)),
]);
const result = (await mergeImg(
await Promise.all(
images.map(async img => {
const JimpImg = await Jimp.read(img);
return {
src: await JimpImg.getBufferAsync('image/png'),
};
})
),
{
direction: true,
}
))
.rgba(false)
.background(0xffffffff);
await fs.ensureDir(dir);
await Promise.all([
result.write(path.join(dir, file)),
...images.map(image => fs.remove(image)),
]);
}

View file

@ -9,29 +9,29 @@ import './app.global.css';
import * as Sentry from '@sentry/electron';
if (remote.getGlobal('process').env.NODE_ENV !== 'development') {
Sentry.init({
dsn: 'https://f2cdbc6a88aa4a068a738d4e4cfd3e12@sentry.io/1553155',
environment: remote.getGlobal('process').env.NODE_ENV,
});
Sentry.init({
dsn: 'https://f2cdbc6a88aa4a068a738d4e4cfd3e12@sentry.io/1553155',
environment: remote.getGlobal('process').env.NODE_ENV,
});
}
const store = configureStore();
render(
<AppContainer>
<Root store={store} history={history} />
</AppContainer>,
document.getElementById('root')
<AppContainer>
<Root store={store} history={history} />
</AppContainer>,
document.getElementById('root')
);
if (module.hot) {
module.hot.accept('./containers/Root', () => {
// eslint-disable-next-line global-require
const NextRoot = require('./containers/Root').default;
render(
<AppContainer>
<NextRoot store={store} history={history} />
</AppContainer>,
document.getElementById('root')
);
});
module.hot.accept('./containers/Root', () => {
// eslint-disable-next-line global-require
const NextRoot = require('./containers/Root').default;
render(
<AppContainer>
<NextRoot store={store} history={history} />
</AppContainer>,
document.getElementById('root')
);
});
}

View file

@ -19,20 +19,20 @@ import MenuBuilder from './menu';
import {ACTIVE_DEVICES} from './constants/settingKeys';
import * as Sentry from '@sentry/electron';
const path = require('path')
const path = require('path');
if (process.env.NODE_ENV !== 'development') {
Sentry.init({
dsn: 'https://f2cdbc6a88aa4a068a738d4e4cfd3e12@sentry.io/1553155',
});
Sentry.init({
dsn: 'https://f2cdbc6a88aa4a068a738d4e4cfd3e12@sentry.io/1553155',
});
}
export default class AppUpdater {
constructor() {
log.transports.file.level = 'info';
autoUpdater.logger = log;
autoUpdater.checkForUpdatesAndNotify();
}
constructor() {
log.transports.file.level = 'info';
autoUpdater.logger = log;
autoUpdater.checkForUpdatesAndNotify();
}
}
let mainWindow = null;
@ -40,25 +40,27 @@ let mainWindow = null;
let httpAuthCallbacks = {};
if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
}
if (
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
require('electron-debug')();
require('electron-debug')();
}
const installExtensions = async () => {
const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
return Promise.all(
extensions.map(name => installer.default(installer[name], forceDownload))
).catch(console.log);
return Promise.all(
extensions.map(name =>
installer.default(installer[name], forceDownload)
)
).catch(console.log);
};
/**
@ -66,54 +68,54 @@ const installExtensions = async () => {
*/
app.on('window-all-closed', () => {
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
if (process.platform !== 'darwin') {
app.quit();
}
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('login', (event, webContents, request, authInfo, callback) => {
event.preventDefault();
const {url} = request;
console.log('Sending HTTP Auth Prompt', {url});
if (httpAuthCallbacks[url]) {
return httpAuthCallbacks[url].push(callback);
}
httpAuthCallbacks[url] = [callback];
mainWindow.webContents.send('http-auth-prompt', {url});
event.preventDefault();
const {url} = request;
console.log('Sending HTTP Auth Prompt', {url});
if (httpAuthCallbacks[url]) {
return httpAuthCallbacks[url].push(callback);
}
httpAuthCallbacks[url] = [callback];
mainWindow.webContents.send('http-auth-prompt', {url});
});
const createWindow = async () => {
if (
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
await installExtensions();
}
if (
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
await installExtensions();
}
const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize;
const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize;
let iconPath=path.resolve(__dirname, '../resources/icons/64x64.png')
mainWindow = new BrowserWindow({
show: false,
width,
height,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
webviewTag: true,
},
titleBarStyle: 'hidden',
icon: iconPath
});
let iconPath = path.resolve(__dirname, '../resources/icons/64x64.png');
mainWindow = new BrowserWindow({
show: false,
width,
height,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
webviewTag: true,
},
titleBarStyle: 'hidden',
icon: iconPath,
});
mainWindow.loadURL(`file://${__dirname}/app.html`);
mainWindow.loadURL(`file://${__dirname}/app.html`);
mainWindow.webContents.on('did-finish-load', function() {
if (process.platform === 'darwin') {
// Trick to make the transparent title bar draggable
mainWindow.webContents.executeJavaScript(`
mainWindow.webContents.on('did-finish-load', function() {
if (process.platform === 'darwin') {
// Trick to make the transparent title bar draggable
mainWindow.webContents.executeJavaScript(`
var div = document.createElement("div");
div.style.position = "absolute";
div.style.top = 0;
@ -123,42 +125,42 @@ const createWindow = async () => {
div.style['-webkit-user-select'] = 'none';
document.body.appendChild(div);
`);
}
});
}
});
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
ipcMain.on('http-auth-promt-response', (event, ...args) => {
if (!args[0].url) {
return;
}
const {url, username, password} = args[0];
if (!httpAuthCallbacks[url]) {
return;
}
httpAuthCallbacks[url].forEach(cb => cb(username, password));
httpAuthCallbacks[url] = null;
});
ipcMain.on('http-auth-promt-response', (event, ...args) => {
if (!args[0].url) {
return;
}
const {url, username, password} = args[0];
if (!httpAuthCallbacks[url]) {
return;
}
httpAuthCallbacks[url].forEach(cb => cb(username, password));
httpAuthCallbacks[url] = null;
});
mainWindow.on('closed', () => {
mainWindow = null;
});
mainWindow.on('closed', () => {
mainWindow = null;
});
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
// Remove this if your app does not use auto updates
// eslint-disable-next-line
new AppUpdater();
// Remove this if your app does not use auto updates
// eslint-disable-next-line
new AppUpdater();
};
app.on('activate', (event, hasVisibleWindows) => {
if (hasVisibleWindows) {
return;
}
createWindow();
if (hasVisibleWindows) {
return;
}
createWindow();
});
app.on('ready', createWindow);

View file

@ -2,276 +2,304 @@
import {app, Menu, shell, BrowserWindow} from 'electron';
export default class MenuBuilder {
mainWindow: BrowserWindow;
mainWindow: BrowserWindow;
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
}
buildMenu() {
if (
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
this.setupDevelopmentEnvironment();
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
}
const template =
process.platform === 'darwin'
? this.buildDarwinTemplate()
: this.buildDefaultTemplate();
buildMenu() {
if (
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
this.setupDevelopmentEnvironment();
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
const template =
process.platform === 'darwin'
? this.buildDarwinTemplate()
: this.buildDefaultTemplate();
return menu;
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
setupDevelopmentEnvironment() {
this.mainWindow.openDevTools();
this.mainWindow.webContents.on('context-menu', (e, props) => {
const {x, y} = props;
return menu;
}
Menu.buildFromTemplate([
{
label: 'Inspect element',
click: () => {
this.mainWindow.inspectElement(x, y);
},
},
]).popup(this.mainWindow);
});
}
setupDevelopmentEnvironment() {
this.mainWindow.openDevTools();
this.mainWindow.webContents.on('context-menu', (e, props) => {
const {x, y} = props;
buildDarwinTemplate() {
const subMenuAbout = {
label: 'Electron',
submenu: [
{
label: 'About ResponsivelyApp',
selector: 'orderFrontStandardAboutPanel:',
},
{type: 'separator'},
{label: 'Services', submenu: []},
{type: 'separator'},
{
label: 'Hide ResponsivelyApp',
accelerator: 'Command+H',
selector: 'hide:',
},
{
label: 'Hide Others',
accelerator: 'Command+Shift+H',
selector: 'hideOtherApplications:',
},
{label: 'Show All', selector: 'unhideAllApplications:'},
{type: 'separator'},
{
label: 'Quit',
accelerator: 'Command+Q',
click: () => {
app.quit();
},
},
],
};
const subMenuEdit = {
label: 'Edit',
submenu: [
{label: 'Undo', accelerator: 'Command+Z', selector: 'undo:'},
{label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:'},
{type: 'separator'},
{label: 'Cut', accelerator: 'Command+X', selector: 'cut:'},
{label: 'Copy', accelerator: 'Command+C', selector: 'copy:'},
{label: 'Paste', accelerator: 'Command+V', selector: 'paste:'},
{
label: 'Select All',
accelerator: 'Command+A',
selector: 'selectAll:',
},
],
};
const subMenuViewDev = {
label: 'View',
submenu: [
{
label: 'Reload',
accelerator: 'Command+R',
click: () => {
this.mainWindow.webContents.reload();
},
},
{
label: 'Toggle Full Screen',
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
},
{
label: 'Toggle Developer Tools',
accelerator: 'Alt+Command+I',
click: () => {
this.mainWindow.toggleDevTools();
},
},
],
};
const subMenuViewProd = {
label: 'View',
submenu: [
{
label: 'Toggle Full Screen',
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
},
],
};
const subMenuWindow = {
label: 'Window',
submenu: [
{
label: 'Minimize',
accelerator: 'Command+M',
selector: 'performMiniaturize:',
},
{label: 'Close', accelerator: 'Command+W', selector: 'performClose:'},
{type: 'separator'},
{label: 'Bring All to Front', selector: 'arrangeInFront:'},
],
};
const subMenuHelp = {
label: 'Help',
submenu: [
{
label: 'Learn More',
click() {
shell.openExternal('http://electron.atom.io');
},
},
{
label: 'Documentation',
click() {
shell.openExternal(
'https://github.com/atom/electron/tree/master/docs#readme'
);
},
},
{
label: 'Community Discussions',
click() {
shell.openExternal('https://discuss.atom.io/c/electron');
},
},
{
label: 'Search Issues',
click() {
shell.openExternal('https://github.com/atom/electron/issues');
},
},
],
};
const subMenuView =
process.env.NODE_ENV === 'development' ? subMenuViewDev : subMenuViewProd;
return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp];
}
buildDefaultTemplate() {
const templateDefault = [
{
label: '&File',
submenu: [
{
label: '&Open',
accelerator: 'Ctrl+O',
},
{
label: '&Close',
accelerator: 'Ctrl+W',
click: () => {
this.mainWindow.close();
},
},
],
},
{
label: '&View',
submenu:
process.env.NODE_ENV === 'development'
? [
Menu.buildFromTemplate([
{
label: '&Reload',
accelerator: 'Ctrl+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: 'Inspect element',
click: () => {
this.mainWindow.inspectElement(x, y);
},
},
]).popup(this.mainWindow);
});
}
buildDarwinTemplate() {
const subMenuAbout = {
label: 'Electron',
submenu: [
{
label: 'About ResponsivelyApp',
selector: 'orderFrontStandardAboutPanel:',
},
{type: 'separator'},
{label: 'Services', submenu: []},
{type: 'separator'},
{
label: 'Hide ResponsivelyApp',
accelerator: 'Command+H',
selector: 'hide:',
},
{
label: 'Toggle &Full Screen',
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen()
);
},
label: 'Hide Others',
accelerator: 'Command+Shift+H',
selector: 'hideOtherApplications:',
},
{label: 'Show All', selector: 'unhideAllApplications:'},
{type: 'separator'},
{
label: 'Quit',
accelerator: 'Command+Q',
click: () => {
app.quit();
},
},
],
};
const subMenuEdit = {
label: 'Edit',
submenu: [
{label: 'Undo', accelerator: 'Command+Z', selector: 'undo:'},
{
label: 'Redo',
accelerator: 'Shift+Command+Z',
selector: 'redo:',
},
{type: 'separator'},
{label: 'Cut', accelerator: 'Command+X', selector: 'cut:'},
{label: 'Copy', accelerator: 'Command+C', selector: 'copy:'},
{label: 'Paste', accelerator: 'Command+V', selector: 'paste:'},
{
label: 'Select All',
accelerator: 'Command+A',
selector: 'selectAll:',
},
],
};
const subMenuViewDev = {
label: 'View',
submenu: [
{
label: 'Reload',
accelerator: 'Command+R',
click: () => {
this.mainWindow.webContents.reload();
},
},
{
label: 'Toggle &Developer Tools',
accelerator: 'Alt+Ctrl+I',
click: () => {
this.mainWindow.toggleDevTools();
},
label: 'Toggle Full Screen',
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen()
);
},
},
]
: [
{
label: 'Toggle &Full Screen',
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen()
);
},
label: 'Toggle Developer Tools',
accelerator: 'Alt+Command+I',
click: () => {
this.mainWindow.toggleDevTools();
},
},
],
},
{
label: 'Help',
submenu: [
{
label: 'Learn More',
click() {
shell.openExternal('http://electron.atom.io');
},
},
{
label: 'Documentation',
click() {
shell.openExternal(
'https://github.com/atom/electron/tree/master/docs#readme'
);
},
},
{
label: 'Community Discussions',
click() {
shell.openExternal('https://discuss.atom.io/c/electron');
},
},
{
label: 'Search Issues',
click() {
shell.openExternal('https://github.com/atom/electron/issues');
},
},
],
},
];
],
};
const subMenuViewProd = {
label: 'View',
submenu: [
{
label: 'Toggle Full Screen',
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen()
);
},
},
],
};
const subMenuWindow = {
label: 'Window',
submenu: [
{
label: 'Minimize',
accelerator: 'Command+M',
selector: 'performMiniaturize:',
},
{
label: 'Close',
accelerator: 'Command+W',
selector: 'performClose:',
},
{type: 'separator'},
{label: 'Bring All to Front', selector: 'arrangeInFront:'},
],
};
const subMenuHelp = {
label: 'Help',
submenu: [
{
label: 'Learn More',
click() {
shell.openExternal('http://electron.atom.io');
},
},
{
label: 'Documentation',
click() {
shell.openExternal(
'https://github.com/atom/electron/tree/master/docs#readme'
);
},
},
{
label: 'Community Discussions',
click() {
shell.openExternal(
'https://discuss.atom.io/c/electron'
);
},
},
{
label: 'Search Issues',
click() {
shell.openExternal(
'https://github.com/atom/electron/issues'
);
},
},
],
};
return templateDefault;
}
const subMenuView =
process.env.NODE_ENV === 'development'
? subMenuViewDev
: subMenuViewProd;
return [
subMenuAbout,
subMenuEdit,
subMenuView,
subMenuWindow,
subMenuHelp,
];
}
buildDefaultTemplate() {
const templateDefault = [
{
label: '&File',
submenu: [
{
label: '&Open',
accelerator: 'Ctrl+O',
},
{
label: '&Close',
accelerator: 'Ctrl+W',
click: () => {
this.mainWindow.close();
},
},
],
},
{
label: '&View',
submenu:
process.env.NODE_ENV === 'development'
? [
{
label: '&Reload',
accelerator: 'Ctrl+R',
click: () => {
this.mainWindow.webContents.reload();
},
},
{
label: 'Toggle &Full Screen',
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen()
);
},
},
{
label: 'Toggle &Developer Tools',
accelerator: 'Alt+Ctrl+I',
click: () => {
this.mainWindow.toggleDevTools();
},
},
]
: [
{
label: 'Toggle &Full Screen',
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen()
);
},
},
],
},
{
label: 'Help',
submenu: [
{
label: 'Learn More',
click() {
shell.openExternal('http://electron.atom.io');
},
},
{
label: 'Documentation',
click() {
shell.openExternal(
'https://github.com/atom/electron/tree/master/docs#readme'
);
},
},
{
label: 'Community Discussions',
click() {
shell.openExternal(
'https://discuss.atom.io/c/electron'
);
},
},
{
label: 'Search Issues',
click() {
shell.openExternal(
'https://github.com/atom/electron/issues'
);
},
},
],
},
];
return templateDefault;
}
}

View file

@ -8,169 +8,169 @@ var menu = new Menu();
var rightClickPosition = null;
menu.append(
new MenuItem({
label: 'Take Screenshot',
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost('takeScreenshot');
},
})
new MenuItem({
label: 'Take Screenshot',
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost('takeScreenshot');
},
})
);
menu.append(
new MenuItem({
label: 'Tilt Device',
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost('tiltDevice');
},
})
new MenuItem({
label: 'Tilt Device',
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost('tiltDevice');
},
})
);
menu.append(
new MenuItem({
id: 'mirror-events',
label: 'Mirror Events',
type: 'checkbox',
checked: true,
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost('toggleEventMirroring');
},
})
new MenuItem({
id: 'mirror-events',
label: 'Mirror Events',
type: 'checkbox',
checked: true,
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost('toggleEventMirroring');
},
})
);
menu.append(new MenuItem({type: 'separator'}));
menu.append(
new MenuItem({
label: 'Inspect',
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost(
'openDevToolsInspector',
rightClickPosition
);
},
})
new MenuItem({
label: 'Inspect',
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost(
'openDevToolsInspector',
rightClickPosition
);
},
})
);
menu.append(
new MenuItem({
label: 'Open Console',
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost('openConsole');
},
})
new MenuItem({
label: 'Open Console',
click: function(menuItem, browserWindow, event) {
window.responsivelyApp.sendMessageToHost('openConsole');
},
})
);
window.addEventListener(
'contextmenu',
function(e) {
e.preventDefault();
rightClickPosition = {x: e.x, y: e.y};
menu.popup(remote.getCurrentWindow());
},
false
'contextmenu',
function(e) {
e.preventDefault();
rightClickPosition = {x: e.x, y: e.y};
menu.popup(remote.getCurrentWindow());
},
false
);
global.responsivelyApp = {
DomInspector: DomInspector,
sendMessageToHost: (type, message) => {
if (!message) {
message = {};
}
message.sourceDeviceId = window.responsivelyApp.deviceId;
ipcRenderer.sendToHost(type, message);
},
cssPath: el => {
if (!(el instanceof Element)) return;
var path = [];
while (el.nodeType === Node.ELEMENT_NODE) {
var selector = el.nodeName.toLowerCase();
var sib = el,
nth = 1;
while (
sib.nodeType === Node.ELEMENT_NODE &&
(sib = sib.previousElementSibling) &&
nth++
);
selector += ':nth-child(' + nth + ')';
path.unshift(selector);
el = el.parentNode;
}
return path.join(' > ');
},
eventFire: (el, etype) => {
if (el.fireEvent) {
el.fireEvent('on' + etype);
} else {
var evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
evObj.responsivelyAppProcessed = true;
el.dispatchEvent(evObj);
}
},
hideFixedPositionElementsForScreenshot: () => {
const elems = document.body.getElementsByTagName('*');
for (const elem of elems) {
const computedStyle = window.getComputedStyle(elem, null);
if (computedStyle.getPropertyValue('position') == 'fixed') {
elem.classList.add('responsivelyApp__HiddenForScreenshot');
}
}
},
unHideElementsHiddenForScreenshot: () => {
const elems = document.body.querySelectorAll(
'.responsivelyApp__HiddenForScreenshot'
);
for (const elem of elems) {
elem.classList.remove('responsivelyApp__HiddenForScreenshot');
}
},
DomInspector: DomInspector,
sendMessageToHost: (type, message) => {
if (!message) {
message = {};
}
message.sourceDeviceId = window.responsivelyApp.deviceId;
ipcRenderer.sendToHost(type, message);
},
cssPath: el => {
if (!(el instanceof Element)) return;
var path = [];
while (el.nodeType === Node.ELEMENT_NODE) {
var selector = el.nodeName.toLowerCase();
var sib = el,
nth = 1;
while (
sib.nodeType === Node.ELEMENT_NODE &&
(sib = sib.previousElementSibling) &&
nth++
);
selector += ':nth-child(' + nth + ')';
path.unshift(selector);
el = el.parentNode;
}
return path.join(' > ');
},
eventFire: (el, etype) => {
if (el.fireEvent) {
el.fireEvent('on' + etype);
} else {
var evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
evObj.responsivelyAppProcessed = true;
el.dispatchEvent(evObj);
}
},
hideFixedPositionElementsForScreenshot: () => {
const elems = document.body.getElementsByTagName('*');
for (const elem of elems) {
const computedStyle = window.getComputedStyle(elem, null);
if (computedStyle.getPropertyValue('position') == 'fixed') {
elem.classList.add('responsivelyApp__HiddenForScreenshot');
}
}
},
unHideElementsHiddenForScreenshot: () => {
const elems = document.body.querySelectorAll(
'.responsivelyApp__HiddenForScreenshot'
);
for (const elem of elems) {
elem.classList.remove('responsivelyApp__HiddenForScreenshot');
}
},
};
ipcRenderer.on('scrollMessage', (event, args) => {
window.scrollTo({
top: args.y,
left: args.x,
});
window.scrollTo({
top: args.y,
left: args.x,
});
});
ipcRenderer.on('clickMessage', (event, args) => {
const elem = document.querySelector(args.cssPath);
if (!elem) {
return;
}
window.responsivelyApp.lastClickElement = elem;
if (elem.click) {
elem.click();
return;
}
window.responsivelyApp.eventFire(elem, 'click');
const elem = document.querySelector(args.cssPath);
if (!elem) {
return;
}
window.responsivelyApp.lastClickElement = elem;
if (elem.click) {
elem.click();
return;
}
window.responsivelyApp.eventFire(elem, 'click');
});
ipcRenderer.on('scrollDownMessage', (event, args) => {
window.scrollTo({
top: window.scrollY + 250,
left: window.scrollX + 250,
behavior: 'smooth',
});
window.scrollTo({
top: window.scrollY + 250,
left: window.scrollX + 250,
behavior: 'smooth',
});
});
ipcRenderer.on('scrollUpMessage', (event, args) => {
window.scrollTo({
top: window.scrollY - 250,
left: window.scrollX - 250,
behavior: 'smooth',
});
window.scrollTo({
top: window.scrollY - 250,
left: window.scrollX - 250,
behavior: 'smooth',
});
});
ipcRenderer.on('enableInspectorMessage', (event, args) => {
responsivelyApp.domInspector = new responsivelyApp.DomInspector();
window.responsivelyApp.domInspector.enable();
window.responsivelyApp.domInspectorEnabled = true;
responsivelyApp.domInspector = new responsivelyApp.DomInspector();
window.responsivelyApp.domInspector.enable();
window.responsivelyApp.domInspectorEnabled = true;
});
ipcRenderer.on('disableInspectorMessage', (event, args) => {
window.responsivelyApp.domInspector.destroy();
window.responsivelyApp.domInspector = null;
window.responsivelyApp.domInspectorEnabled = false;
window.responsivelyApp.domInspector.destroy();
window.responsivelyApp.domInspector = null;
window.responsivelyApp.domInspectorEnabled = false;
});
ipcRenderer.on('eventsMirroringState', (event, args) => {
menu.getMenuItemById('mirror-events').checked = args;
menu.getMenuItemById('mirror-events').checked = args;
});

View file

@ -1,23 +1,23 @@
// @flow
import {
NEW_ADDRESS,
NEW_ZOOM_LEVEL,
NEW_SCROLL_POSITION,
NEW_NAVIGATOR_STATUS,
NEW_DRAWER_CONTENT,
NEW_PREVIEWER_CONFIG,
NEW_ACTIVE_DEVICES,
NEW_ACTIVE_DEVICE,
NEW_FILTERS,
NEW_HOMEPAGE,
NEW_ADDRESS,
NEW_ZOOM_LEVEL,
NEW_SCROLL_POSITION,
NEW_NAVIGATOR_STATUS,
NEW_DRAWER_CONTENT,
NEW_PREVIEWER_CONFIG,
NEW_ACTIVE_DEVICES,
NEW_ACTIVE_DEVICE,
NEW_FILTERS,
NEW_HOMEPAGE,
} from '../actions/browser';
import type {Action} from './types';
import devices from '../constants/devices';
import settings from 'electron-settings';
import type {Device} from '../constants/devices';
import {
FLEXIGRID_LAYOUT,
INDIVIDUAL_LAYOUT,
FLEXIGRID_LAYOUT,
INDIVIDUAL_LAYOUT,
} from '../constants/previewerLayouts';
import {DEVICE_MANAGER} from '../constants/DrawerContents';
import {ACTIVE_DEVICES} from '../constants/settingKeys';
@ -25,27 +25,27 @@ import {isIfStatement} from 'typescript';
import {getHomepage, saveHomepage} from '../utils/navigatorUtils';
export const FILTER_FIELDS = {
OS: 'OS',
DEVICE_TYPE: 'DEVICE_TYPE',
OS: 'OS',
DEVICE_TYPE: 'DEVICE_TYPE',
};
type ScrollPositionType = {
x: number,
y: number,
x: number,
y: number,
};
type NavigatorStatusType = {
backEnabled: boolean,
forwardEnabled: boolean,
backEnabled: boolean,
forwardEnabled: boolean,
};
type DrawerType = {
open: boolean,
content: string,
open: boolean,
content: string,
};
type PreviewerType = {
layout: string,
layout: string,
};
type FilterFieldType = FILTER_FIELDS.OS | FILTER_FIELDS.DEVICE_TYPE;
@ -53,93 +53,93 @@ type FilterFieldType = FILTER_FIELDS.OS | FILTER_FIELDS.DEVICE_TYPE;
type FilterType = {[key: FilterFieldType]: Array<string>};
export type BrowserStateType = {
devices: Array<Device>,
homepage: string,
address: string,
zoomLevel: number,
scrollPosition: ScrollPositionType,
navigatorStatus: NavigatorStatusType,
drawer: DrawerType,
previewer: PreviewerType,
filters: FilterType,
devices: Array<Device>,
homepage: string,
address: string,
zoomLevel: number,
scrollPosition: ScrollPositionType,
navigatorStatus: NavigatorStatusType,
drawer: DrawerType,
previewer: PreviewerType,
filters: FilterType,
};
let _activeDevices = null;
function _saveActiveDevices(devices) {
settings.set(ACTIVE_DEVICES, devices);
_activeDevices = devices;
settings.set(ACTIVE_DEVICES, devices);
_activeDevices = devices;
}
function _getActiveDevices() {
if (_activeDevices) {
return _activeDevices;
}
let activeDevices = settings.get(ACTIVE_DEVICES);
if (!activeDevices) {
activeDevices = devices.filter(device => device.added);
_saveActiveDevices(activeDevices);
}
return activeDevices;
if (_activeDevices) {
return _activeDevices;
}
let activeDevices = settings.get(ACTIVE_DEVICES);
if (!activeDevices) {
activeDevices = devices.filter(device => device.added);
_saveActiveDevices(activeDevices);
}
return activeDevices;
}
export default function counter(
state: BrowserStateType = {
devices: _getActiveDevices(),
homepage: getHomepage(),
address: getHomepage(),
zoomLevel: 0.6,
previousZoomLevel: null,
scrollPosition: {x: 0, y: 0},
navigatorStatus: {backEnabled: false, forwardEnabled: false},
drawer: {open: true, content: DEVICE_MANAGER},
previewer: {layout: FLEXIGRID_LAYOUT},
filters: {[FILTER_FIELDS.OS]: [], [FILTER_FIELDS.DEVICE_TYPE]: []},
},
action: Action
state: BrowserStateType = {
devices: _getActiveDevices(),
homepage: getHomepage(),
address: getHomepage(),
zoomLevel: 0.6,
previousZoomLevel: null,
scrollPosition: {x: 0, y: 0},
navigatorStatus: {backEnabled: false, forwardEnabled: false},
drawer: {open: true, content: DEVICE_MANAGER},
previewer: {layout: FLEXIGRID_LAYOUT},
filters: {[FILTER_FIELDS.OS]: [], [FILTER_FIELDS.DEVICE_TYPE]: []},
},
action: Action
) {
switch (action.type) {
case NEW_ADDRESS:
return {...state, address: action.address};
case NEW_HOMEPAGE:
const {homepage} = action;
saveHomepage(homepage);
return {...state, homepage};
case NEW_ZOOM_LEVEL:
return {...state, zoomLevel: action.zoomLevel};
case NEW_SCROLL_POSITION:
return {...state, scrollPosition: action.scrollPosition};
case NEW_NAVIGATOR_STATUS:
return {...state, navigatorStatus: action.navigatorStatus};
case NEW_DRAWER_CONTENT:
return {...state, drawer: action.drawer};
case NEW_PREVIEWER_CONFIG:
const updateObject = {previewer: action.previewer};
if (
state.previewer.layout !== INDIVIDUAL_LAYOUT &&
action.previewer.layout === INDIVIDUAL_LAYOUT
) {
updateObject.zoomLevel = 1;
updateObject.previousZoomLevel = state.zoomLevel;
}
if (
state.previewer.layout === INDIVIDUAL_LAYOUT &&
action.previewer.layout !== INDIVIDUAL_LAYOUT
) {
updateObject.zoomLevel = state.previousZoomLevel;
updateObject.previousZoomLevel = null;
}
return {...state, ...updateObject};
case NEW_ACTIVE_DEVICES:
_saveActiveDevices(action.devices);
return {...state, devices: action.devices};
case NEW_ACTIVE_DEVICE:
const devices = [...state.devices, action.device];
_saveActiveDevices(devices);
return {...state, devices};
case NEW_FILTERS:
return {...state, filters: action.filters};
default:
return state;
}
switch (action.type) {
case NEW_ADDRESS:
return {...state, address: action.address};
case NEW_HOMEPAGE:
const {homepage} = action;
saveHomepage(homepage);
return {...state, homepage};
case NEW_ZOOM_LEVEL:
return {...state, zoomLevel: action.zoomLevel};
case NEW_SCROLL_POSITION:
return {...state, scrollPosition: action.scrollPosition};
case NEW_NAVIGATOR_STATUS:
return {...state, navigatorStatus: action.navigatorStatus};
case NEW_DRAWER_CONTENT:
return {...state, drawer: action.drawer};
case NEW_PREVIEWER_CONFIG:
const updateObject = {previewer: action.previewer};
if (
state.previewer.layout !== INDIVIDUAL_LAYOUT &&
action.previewer.layout === INDIVIDUAL_LAYOUT
) {
updateObject.zoomLevel = 1;
updateObject.previousZoomLevel = state.zoomLevel;
}
if (
state.previewer.layout === INDIVIDUAL_LAYOUT &&
action.previewer.layout !== INDIVIDUAL_LAYOUT
) {
updateObject.zoomLevel = state.previousZoomLevel;
updateObject.previousZoomLevel = null;
}
return {...state, ...updateObject};
case NEW_ACTIVE_DEVICES:
_saveActiveDevices(action.devices);
return {...state, devices: action.devices};
case NEW_ACTIVE_DEVICE:
const devices = [...state.devices, action.device];
_saveActiveDevices(devices);
return {...state, devices};
case NEW_FILTERS:
return {...state, filters: action.filters};
default:
return state;
}
}

View file

@ -4,8 +4,8 @@ import {connectRouter} from 'connected-react-router';
import browser from './browser';
export default function createRootReducer(history: History) {
return combineReducers({
router: connectRouter(history),
browser,
});
return combineReducers({
router: connectRouter(history),
browser,
});
}

View file

@ -4,15 +4,15 @@ import type {BrowserStateType as _BrowserStateType} from './browser';
export type BrowserStateType = _BrowserStateType;
export type counterStateType = {
+counter: number,
+counter: number,
};
export type RootStateType = {
browser: BrowserStateType,
browser: BrowserStateType,
};
export type Action = {
+type: string,
+type: string,
};
export type GetState = () => RootStateType;

View file

@ -10,58 +10,58 @@ const history = createHashHistory();
const rootReducer = createRootReducer(history);
const configureStore = initialState => {
// Redux Configuration
const middleware = [];
const enhancers = [];
// Redux Configuration
const middleware = [];
const enhancers = [];
// Thunk Middleware
middleware.push(thunk);
// Thunk Middleware
middleware.push(thunk);
// Logging Middleware
const logger = createLogger({
level: 'info',
collapsed: true,
});
// Logging Middleware
const logger = createLogger({
level: 'info',
collapsed: true,
});
// Skip redux logs in console during the tests
if (process.env.NODE_ENV !== 'test') {
middleware.push(logger);
}
// Skip redux logs in console during the tests
if (process.env.NODE_ENV !== 'test') {
middleware.push(logger);
}
// Router Middleware
const router = routerMiddleware(history);
middleware.push(router);
// Router Middleware
const router = routerMiddleware(history);
middleware.push(router);
// Redux DevTools Configuration
const actionCreators = {
...routerActions,
};
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Options: http://extension.remotedev.io/docs/API/Arguments.html
actionCreators,
})
: compose;
/* eslint-enable no-underscore-dangle */
// Redux DevTools Configuration
const actionCreators = {
...routerActions,
};
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Options: http://extension.remotedev.io/docs/API/Arguments.html
actionCreators,
})
: compose;
/* eslint-enable no-underscore-dangle */
// Apply Middleware & Compose Enhancers
enhancers.push(applyMiddleware(...middleware));
const enhancer = composeEnhancers(...enhancers);
// Apply Middleware & Compose Enhancers
enhancers.push(applyMiddleware(...middleware));
const enhancer = composeEnhancers(...enhancers);
// Create Store
const store = createStore(rootReducer, initialState, enhancer);
// Create Store
const store = createStore(rootReducer, initialState, enhancer);
if (module.hot) {
module.hot.accept(
'../reducers',
// eslint-disable-next-line global-require
() => store.replaceReducer(require('../reducers').default)
);
}
if (module.hot) {
module.hot.accept(
'../reducers',
// eslint-disable-next-line global-require
() => store.replaceReducer(require('../reducers').default)
);
}
return store;
return store;
};
export default {configureStore, history};

View file

@ -3,10 +3,10 @@ import configureStoreDev from './configureStore.dev';
import configureStoreProd from './configureStore.prod';
const selectedConfigureStore =
process.env.NODE_ENV === 'production'
? configureStoreProd
: configureStoreDev;
process.env.NODE_ENV === 'production'
? configureStoreProd
: configureStoreDev;
export const { configureStore } = selectedConfigureStore;
export const {configureStore} = selectedConfigureStore;
export const { history } = selectedConfigureStore;
export const {history} = selectedConfigureStore;

View file

@ -11,7 +11,7 @@ const router = routerMiddleware(history);
const enhancer = applyMiddleware(thunk, router);
function configureStore(initialState) {
return createStore(rootReducer, initialState, enhancer);
return createStore(rootReducer, initialState, enhancer);
}
export default {configureStore, history};

View file

@ -1,22 +1,25 @@
import {FILTER_FIELDS} from '../reducers/browser';
export const isDeviceEligible = (device, filterCriteria) => {
if (Object.keys(filterCriteria[FILTER_FIELDS.OS]).length > 0) {
console.log(
'OS',
device.os,
filterCriteria[FILTER_FIELDS.OS].indexOf(device.os)
);
if (filterCriteria[FILTER_FIELDS.OS].indexOf(device.os) === -1) {
return false;
if (Object.keys(filterCriteria[FILTER_FIELDS.OS]).length > 0) {
console.log(
'OS',
device.os,
filterCriteria[FILTER_FIELDS.OS].indexOf(device.os)
);
if (filterCriteria[FILTER_FIELDS.OS].indexOf(device.os) === -1) {
return false;
}
}
}
if (Object.keys(filterCriteria[FILTER_FIELDS.DEVICE_TYPE]).length > 0) {
if (filterCriteria[FILTER_FIELDS.DEVICE_TYPE].indexOf(device.type) === -1) {
return false;
if (Object.keys(filterCriteria[FILTER_FIELDS.DEVICE_TYPE]).length > 0) {
if (
filterCriteria[FILTER_FIELDS.DEVICE_TYPE].indexOf(device.type) ===
-1
) {
return false;
}
}
}
return true;
return true;
};

View file

@ -1,5 +1,5 @@
const emailRE = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
export function validateEmail(email) {
return emailRE.test(String(email).toLowerCase());
return emailRE.test(String(email).toLowerCase());
}

View file

@ -9,39 +9,39 @@ import WindowsIcon from '../components/icons/Windows';
import AndroidIcon from '@material-ui/icons/Android';
export const getDeviceIcon = deviceType => {
const iconProps = {
color: iconsColor,
style: {fontSize: 'inherit', paddingRight: 2},
};
switch (deviceType) {
case DEVICE_TYPE.phone:
return <MobileIcon {...iconProps} />;
case DEVICE_TYPE.tablet:
return <TabletIcon {...iconProps} />;
case DEVICE_TYPE.desktop:
return <DesktopIcon {...iconProps} />;
default:
return null;
}
const iconProps = {
color: iconsColor,
style: {fontSize: 'inherit', paddingRight: 2},
};
switch (deviceType) {
case DEVICE_TYPE.phone:
return <MobileIcon {...iconProps} />;
case DEVICE_TYPE.tablet:
return <TabletIcon {...iconProps} />;
case DEVICE_TYPE.desktop:
return <DesktopIcon {...iconProps} />;
default:
return null;
}
};
export const getOSIcon = (os, _color) => {
const color = _color || iconsColor;
const iconProps = {
color,
style: {fontSize: 'inherit', paddingRight: 2, color},
height: '1em',
};
switch (os) {
case OS.ios:
return <AppleIcon {...iconProps} />;
case OS.android:
return <AndroidIcon {...iconProps} />;
case OS.windowsPhone:
return <WindowsIcon {...iconProps} height="0.8em" />;
case OS.pc:
return <DesktopIcon {...iconProps} />;
default:
return null;
}
const color = _color || iconsColor;
const iconProps = {
color,
style: {fontSize: 'inherit', paddingRight: 2, color},
height: '1em',
};
switch (os) {
case OS.ios:
return <AppleIcon {...iconProps} />;
case OS.android:
return <AndroidIcon {...iconProps} />;
case OS.windowsPhone:
return <WindowsIcon {...iconProps} height="0.8em" />;
case OS.pc:
return <DesktopIcon {...iconProps} />;
default:
return null;
}
};

View file

@ -4,9 +4,9 @@ import path from 'path';
const HOME_PAGE = 'HOME_PAGE';
export function saveHomepage(url) {
settings.set(HOME_PAGE, url);
settings.set(HOME_PAGE, url);
}
export function getHomepage() {
return settings.get(HOME_PAGE) || 'https://www.google.com';
return settings.get(HOME_PAGE) || 'https://www.google.com';
}

View file

@ -3,68 +3,73 @@
const developmentEnvironments = ['development', 'test'];
const developmentPlugins = [
require('@babel/plugin-transform-classes'),
require('react-hot-loader/babel'),
require('@babel/plugin-transform-classes'),
require('react-hot-loader/babel'),
];
const productionPlugins = [
require('babel-plugin-dev-expression'),
require('babel-plugin-dev-expression'),
// babel-preset-react-optimize
require('@babel/plugin-transform-react-constant-elements'),
require('@babel/plugin-transform-react-inline-elements'),
require('babel-plugin-transform-react-remove-prop-types'),
// babel-preset-react-optimize
require('@babel/plugin-transform-react-constant-elements'),
require('@babel/plugin-transform-react-inline-elements'),
require('babel-plugin-transform-react-remove-prop-types'),
];
module.exports = api => {
// see docs about api at https://babeljs.io/docs/en/config-files#apicache
// see docs about api at https://babeljs.io/docs/en/config-files#apicache
const development = api.env(developmentEnvironments);
const development = api.env(developmentEnvironments);
return {
presets: [
[
require('@babel/preset-env'),
{
targets: {electron: require('electron/package.json').version},
useBuiltIns: 'usage',
},
],
require('@babel/preset-flow'),
[require('@babel/preset-react'), {development}],
],
plugins: [
// Stage 0
require('@babel/plugin-proposal-function-bind'),
return {
presets: [
[
require('@babel/preset-env'),
{
targets: {
electron: require('electron/package.json').version,
},
useBuiltIns: 'usage',
},
],
require('@babel/preset-flow'),
[require('@babel/preset-react'), {development}],
],
plugins: [
// Stage 0
require('@babel/plugin-proposal-function-bind'),
// Stage 1
require('@babel/plugin-proposal-export-default-from'),
require('@babel/plugin-proposal-logical-assignment-operators'),
[require('@babel/plugin-proposal-optional-chaining'), {loose: false}],
[
require('@babel/plugin-proposal-pipeline-operator'),
{proposal: 'minimal'},
],
[
require('@babel/plugin-proposal-nullish-coalescing-operator'),
{loose: false},
],
require('@babel/plugin-proposal-do-expressions'),
// Stage 1
require('@babel/plugin-proposal-export-default-from'),
require('@babel/plugin-proposal-logical-assignment-operators'),
[
require('@babel/plugin-proposal-optional-chaining'),
{loose: false},
],
[
require('@babel/plugin-proposal-pipeline-operator'),
{proposal: 'minimal'},
],
[
require('@babel/plugin-proposal-nullish-coalescing-operator'),
{loose: false},
],
require('@babel/plugin-proposal-do-expressions'),
// Stage 2
[require('@babel/plugin-proposal-decorators'), {legacy: true}],
require('@babel/plugin-proposal-function-sent'),
require('@babel/plugin-proposal-export-namespace-from'),
require('@babel/plugin-proposal-numeric-separator'),
require('@babel/plugin-proposal-throw-expressions'),
// Stage 2
[require('@babel/plugin-proposal-decorators'), {legacy: true}],
require('@babel/plugin-proposal-function-sent'),
require('@babel/plugin-proposal-export-namespace-from'),
require('@babel/plugin-proposal-numeric-separator'),
require('@babel/plugin-proposal-throw-expressions'),
// Stage 3
require('@babel/plugin-syntax-dynamic-import'),
require('@babel/plugin-syntax-import-meta'),
[require('@babel/plugin-proposal-class-properties'), {loose: true}],
require('@babel/plugin-proposal-json-strings'),
// Stage 3
require('@babel/plugin-syntax-dynamic-import'),
require('@babel/plugin-syntax-import-meta'),
[require('@babel/plugin-proposal-class-properties'), {loose: true}],
require('@babel/plugin-proposal-json-strings'),
...(development ? developmentPlugins : productionPlugins),
],
};
...(development ? developmentPlugins : productionPlugins),
],
};
};

View file

@ -4,44 +4,44 @@
import path from 'path';
import webpack from 'webpack';
import { dependencies } from '../package.json';
import {dependencies} from '../package.json';
export default {
externals: [...Object.keys(dependencies || {})],
externals: [...Object.keys(dependencies || {})],
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
}
]
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
},
],
},
output: {
path: path.join(__dirname, '..', 'app'),
// https://github.com/webpack/webpack/issues/1114
libraryTarget: 'commonjs2'
},
output: {
path: path.join(__dirname, '..', 'app'),
// https://github.com/webpack/webpack/issues/1114
libraryTarget: 'commonjs2',
},
/**
* Determine the array of extensions that should be used to resolve modules.
*/
resolve: {
extensions: ['.js', '.jsx', '.json']
},
/**
* Determine the array of extensions that should be used to resolve modules.
*/
resolve: {
extensions: ['.js', '.jsx', '.json'],
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'production'
}),
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
}),
new webpack.NamedModulesPlugin()
]
new webpack.NamedModulesPlugin(),
],
};

View file

@ -6,68 +6,68 @@ import path from 'path';
import webpack from 'webpack';
import merge from 'webpack-merge';
import TerserPlugin from 'terser-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer';
import baseConfig from './webpack.config.base';
import CheckNodeEnv from '../internals/scripts/CheckNodeEnv';
CheckNodeEnv('production');
export default merge.smart(baseConfig, {
devtool: 'source-map',
devtool: 'source-map',
mode: 'production',
mode: 'production',
target: 'electron-main',
target: 'electron-main',
entry: './app/main.dev',
entry: './app/main.dev',
output: {
path: path.join(__dirname, '..'),
filename: './app/main.prod.js'
},
output: {
path: path.join(__dirname, '..'),
filename: './app/main.prod.js',
},
optimization: {
minimizer: process.env.E2E_BUILD
? []
: [
new TerserPlugin({
parallel: true,
sourceMap: true,
cache: true
})
]
},
optimization: {
minimizer: process.env.E2E_BUILD
? []
: [
new TerserPlugin({
parallel: true,
sourceMap: true,
cache: true,
}),
],
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode:
process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled',
openAnalyzer: process.env.OPEN_ANALYZER === 'true'
}),
plugins: [
new BundleAnalyzerPlugin({
analyzerMode:
process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled',
openAnalyzer: process.env.OPEN_ANALYZER === 'true',
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: false,
START_MINIMIZED: false,
}),
],
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: false,
START_MINIMIZED: false
})
],
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false
}
node: {
__dirname: false,
__filename: false,
},
});

Some files were not shown because too many files have changed in this diff Show more