mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2024-11-10 19:56:54 +03:00
Move zalmoxisus/remotedev-app
This commit is contained in:
parent
8449c6a9fd
commit
9e9146690c
4
packages/redux-devtools-app/.babelrc
Normal file
4
packages/redux-devtools-app/.babelrc
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"presets": [ "es2015", "stage-0", "react" ],
|
||||
"plugins": [ "add-module-exports", "transform-decorators-legacy" ]
|
||||
}
|
51
packages/redux-devtools-app/.eslintrc
Normal file
51
packages/redux-devtools-app/.eslintrc
Normal file
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"extends": "eslint-config-airbnb",
|
||||
"globals": {
|
||||
"chrome": true,
|
||||
"electron": true
|
||||
},
|
||||
"env": {
|
||||
"jest": true,
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"react/prefer-stateless-function": 0,
|
||||
"react/no-array-index-key": 0,
|
||||
"react/forbid-prop-types": 0,
|
||||
"react/require-default-props": 0,
|
||||
"react/jsx-filename-extension": 0,
|
||||
"react/jsx-uses-react": 2,
|
||||
"react/jsx-uses-vars": 2,
|
||||
"react/react-in-jsx-scope": 2,
|
||||
"react/sort-comp": 0,
|
||||
"react/jsx-quotes": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"block-scoped-var": 0,
|
||||
"padded-blocks": 0,
|
||||
"quotes": [ 1, "single" ],
|
||||
"comma-style": [ 2, "last" ],
|
||||
"eol-last": 0,
|
||||
"no-unused-vars": 0,
|
||||
"no-console": 0,
|
||||
"func-names": 0,
|
||||
"prefer-const": 0,
|
||||
"comma-dangle": 0,
|
||||
"id-length": 0,
|
||||
"no-use-before-define": 0,
|
||||
"indent": [2, 2, {"SwitchCase": 1}],
|
||||
"new-cap": [2, { "capIsNewExceptions": ["Test"] }],
|
||||
"no-underscore-dangle": 0,
|
||||
"no-plusplus": 0,
|
||||
"arrow-parens": 0,
|
||||
"prefer-template": 0,
|
||||
"class-methods-use-this": 0,
|
||||
"max-len": ["error", { "code": 120 }],
|
||||
"no-mixed-operators": 0,
|
||||
"no-undef": 0
|
||||
},
|
||||
"plugins": [
|
||||
"react"
|
||||
]
|
||||
}
|
21
packages/redux-devtools-app/LICENSE
Normal file
21
packages/redux-devtools-app/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Mihail Diordiev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
44
packages/redux-devtools-app/README.md
Normal file
44
packages/redux-devtools-app/README.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
Remote Redux DevTools monitor app
|
||||
==================================
|
||||
|
||||
![Demo](https://raw.githubusercontent.com/zalmoxisus/remote-redux-devtools/master/demo.gif)
|
||||
|
||||
Web, Electron and Chrome app for monitoring [remote-redux-devtools](https://github.com/zalmoxisus/remote-redux-devtools). Can be accessed on [`remotedev.io`](http://remotedev.io/local).
|
||||
|
||||
Also it's a react component you can use to build amazing monitor applications like:
|
||||
|
||||
* [redux-devtools-extension](https://github.com/zalmoxisus/redux-devtools-extension).
|
||||
* [react-native-debugger](https://github.com/jhen0409/react-native-debugger) - Electron app, which already includes `remotedev-server`, `remotedev-app` and even React DevTools.
|
||||
* [remote-redux-devtools-on-debugger](https://github.com/jhen0409/remote-redux-devtools-on-debugger) - Used in React Native debugger as a dock monitor.
|
||||
* [atom-redux-devtools](https://github.com/zalmoxisus/atom-redux-devtools) - Used in Atom editor.
|
||||
* [vscode-redux-devtools](https://github.com/jkzing/vscode-redux-devtools) - Used in Visual Studio Code.
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
import DevToolsApp from 'remotedev-app';
|
||||
|
||||
ReactDom.render(
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
* `socketOptions` - *object* used to specify predefined options for the connection:
|
||||
* `hostname` - *string*
|
||||
* `port` - *number or string*
|
||||
* `autoReconnect` - *boolean*
|
||||
* `secure` - *boolean*.
|
||||
* `monitorOptions` - *object* used to specify predefined monitor options:
|
||||
* `selected` - *string* - which monitor is selected by default. One of the following values: `LogMonitor`, `InspectorMonitor`, `ChartMonitor`.
|
||||
* `testTemplates` - *array* of strings representing predefined test templates.
|
||||
* `noSettings` - *boolean* set to `true` in order to hide settings button and dialog.
|
||||
|
||||
### License
|
||||
|
||||
MIT
|
201
packages/redux-devtools-app/assets/electron/index.js
Normal file
201
packages/redux-devtools-app/assets/electron/index.js
Normal file
|
@ -0,0 +1,201 @@
|
|||
const { app, BrowserWindow, Menu, shell } = require('electron');
|
||||
|
||||
let menu;
|
||||
let template;
|
||||
let win = null;
|
||||
|
||||
// require('electron-debug')();
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit();
|
||||
});
|
||||
|
||||
app.on('ready', () => {
|
||||
win = new BrowserWindow({ width: 1024, height: 728 });
|
||||
|
||||
win.loadURL(`file://${__dirname}/index.html`);
|
||||
|
||||
win.on('closed', () => {
|
||||
win = null;
|
||||
});
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
template = [{
|
||||
label: 'Electron',
|
||||
submenu: [{
|
||||
label: 'About',
|
||||
selector: 'orderFrontStandardAboutPanel:'
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Services',
|
||||
submenu: []
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Hide',
|
||||
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();
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
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:'
|
||||
}]
|
||||
}, {
|
||||
label: 'View',
|
||||
submenu: (process.env.NODE_ENV === 'development') ? [{
|
||||
label: 'Reload',
|
||||
accelerator: 'Command+R',
|
||||
click() {
|
||||
win.restart();
|
||||
}
|
||||
}, {
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: 'Ctrl+Command+F',
|
||||
click() {
|
||||
win.setFullScreen(!win.isFullScreen());
|
||||
}
|
||||
}, {
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: 'Alt+Command+I',
|
||||
click() {
|
||||
win.toggleDevTools();
|
||||
}
|
||||
}] : [{
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: 'Ctrl+Command+F',
|
||||
click() {
|
||||
win.setFullScreen(!win.isFullScreen());
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
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:'
|
||||
}]
|
||||
}, {
|
||||
label: 'Help',
|
||||
submenu: [{
|
||||
label: 'Learn More',
|
||||
click() {
|
||||
shell.openExternal('http://electron.atom.io');
|
||||
}
|
||||
}, {
|
||||
label: 'Search Issues',
|
||||
click() {
|
||||
shell.openExternal('https://github.com/atom/electron/issues');
|
||||
}
|
||||
}]
|
||||
}];
|
||||
|
||||
menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
} else {
|
||||
template = [{
|
||||
label: '&File',
|
||||
submenu: [{
|
||||
label: '&Open',
|
||||
accelerator: 'Ctrl+O'
|
||||
}, {
|
||||
label: '&Close',
|
||||
accelerator: 'Ctrl+W',
|
||||
click() {
|
||||
win.close();
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
label: '&View',
|
||||
submenu: (process.env.NODE_ENV === 'development') ? [{
|
||||
label: '&Reload',
|
||||
accelerator: 'Ctrl+R',
|
||||
click() {
|
||||
win.restart();
|
||||
}
|
||||
}, {
|
||||
label: 'Toggle &Full Screen',
|
||||
accelerator: 'F11',
|
||||
click() {
|
||||
win.setFullScreen(!win.isFullScreen());
|
||||
}
|
||||
}, {
|
||||
label: 'Toggle &Developer Tools',
|
||||
accelerator: 'Alt+Ctrl+I',
|
||||
click() {
|
||||
win.toggleDevTools();
|
||||
}
|
||||
}] : [{
|
||||
label: 'Toggle &Full Screen',
|
||||
accelerator: 'F11',
|
||||
click() {
|
||||
win.setFullScreen(!win.isFullScreen());
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
label: 'Help',
|
||||
submenu: [{
|
||||
label: 'Learn More',
|
||||
click() {
|
||||
shell.openExternal('http://electron.atom.io');
|
||||
}
|
||||
}, {
|
||||
label: 'Search Issues',
|
||||
click() {
|
||||
shell.openExternal('https://github.com/atom/electron/issues');
|
||||
}
|
||||
}]
|
||||
}];
|
||||
menu = Menu.buildFromTemplate(template);
|
||||
win.setMenu(menu);
|
||||
}
|
||||
});
|
9
packages/redux-devtools-app/assets/electron/package.json
Normal file
9
packages/redux-devtools-app/assets/electron/package.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "remotedev",
|
||||
"productName": "RemoteDev",
|
||||
"version": "0.0.1",
|
||||
"electronVersion": "1.4.5",
|
||||
"main": "index.js",
|
||||
"description": "Remote Redux DevTools",
|
||||
"authors": "Mihail Diordiev"
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"osx" : {
|
||||
"title": "RemoteDev",
|
||||
"background": "osx/installer.png",
|
||||
"icon": "osx/icon.icns",
|
||||
"icon-size": 225,
|
||||
"contents": [
|
||||
{ "x": 290, "y": 1, "type": "link", "path": "/Applications" },
|
||||
{ "x": 1, "y": 1, "type": "file" }
|
||||
]
|
||||
},
|
||||
"win" : {
|
||||
"title" : "RemoteDev",
|
||||
"version" : "0.0.0.1",
|
||||
"icon" : "windows/icon.ico"
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 97 KiB |
45
packages/redux-devtools-app/assets/index.html
Normal file
45
packages/redux-devtools-app/assets/index.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
|
||||
<title>RemoteDev</title>
|
||||
<style>
|
||||
body {
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media print {
|
||||
@page {
|
||||
size: auto;
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
position: static;
|
||||
}
|
||||
#root > div > div:not(:nth-child(2)) {
|
||||
display: none !important;
|
||||
}
|
||||
#root > div > div:nth-child(2) {
|
||||
overflow: visible !important;
|
||||
position: absolute !important;
|
||||
z-index: 2147483647;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
#root > div > div:nth-child(2) * {
|
||||
overflow: visible !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id='root'></div>
|
||||
</body>
|
||||
</html>
|
112
packages/redux-devtools-app/package.json
Normal file
112
packages/redux-devtools-app/package.json
Normal file
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
"name": "remotedev-app",
|
||||
"version": "0.11.0-3",
|
||||
"description": "Remote Redux DevTools web, electron and chrome app.",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --hot --inline --env.development --env.platform=web --progress",
|
||||
"start:electron": "npm run build:electron && electron ./build/electron",
|
||||
"build:electron": "rimraf ./build/electron && webpack -p --env.platform=electron --progress",
|
||||
"build:web": "rimraf ./build/web && webpack -p --env.platform=web --progress",
|
||||
"build:umd": "rimraf ./dist && webpack --env.development --progress --config webpack.config.umd.js",
|
||||
"build:umd:min": "webpack -p --progress --config webpack.config.umd.js",
|
||||
"clean": "rimraf ./build",
|
||||
"build": "rimraf ./lib && babel ./src/app --out-dir lib",
|
||||
"prepublish": "eslint ./src/app && npm run test && npm run build && npm run build:umd && npm run build:umd:min",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"test": "NODE_ENV=test jest --no-cache"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"dist"
|
||||
],
|
||||
"jest": {
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|scss)$": "<rootDir>/test/__mocks__/styleMock.js"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zalmoxisus/remotedev-app"
|
||||
},
|
||||
"homepage": "https://github.com/zalmoxisus/remotedev-app",
|
||||
"keywords": [
|
||||
"react",
|
||||
"redux",
|
||||
"devtools"
|
||||
],
|
||||
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.22.2",
|
||||
"babel-core": "^6.22.1",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "^6.2.10",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-plugin-react-transform": "^2.0.2",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-preset-env": "^1.1.8",
|
||||
"babel-preset-es2015": "^6.22.0",
|
||||
"babel-preset-react": "^6.22.0",
|
||||
"babel-preset-stage-0": "^6.22.0",
|
||||
"babel-register": "^6.22.0",
|
||||
"chromedriver": "^2.20.0",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"css-loader": "^0.26.1",
|
||||
"electron": "^1.4.5",
|
||||
"electron-builder": "^2.5.0",
|
||||
"electron-debug": "^1.1.0",
|
||||
"electron-packager": "^8.2.0",
|
||||
"enzyme": "^2.8.2",
|
||||
"enzyme-to-json": "^1.5.1",
|
||||
"eslint": "^3.15.0",
|
||||
"eslint-config-airbnb": "^14.1.0",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-jsx-a11y": "^4.0.0",
|
||||
"eslint-plugin-react": "^6.9.0",
|
||||
"file-loader": "^0.10.0",
|
||||
"html-loader": "^0.4.4",
|
||||
"html-webpack-plugin": "^2.28.0",
|
||||
"jest": "^20.0.4",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react": "^15.5.4",
|
||||
"react-addons-test-utils": "15.4.0",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-test-renderer": "^15.5.4",
|
||||
"redux-immutable-state-invariant": "^1.2.0",
|
||||
"redux-logger": "^2.2.1",
|
||||
"rimraf": "^2.5.4",
|
||||
"style-loader": "^0.13.0",
|
||||
"url-loader": "^0.5.7",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-dev-server": "^2.3.0",
|
||||
"webpack-hot-middleware": "^2.16.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-state-visualizer": "^1.3.1",
|
||||
"devui": "^1.0.0-2",
|
||||
"javascript-stringify": "^1.5.0",
|
||||
"jsan": "^3.1.9",
|
||||
"jsondiffpatch": "^0.2.4",
|
||||
"localforage": "^1.5.0",
|
||||
"lodash": "^4.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react-icons": "^2.2.5",
|
||||
"react-redux": "^5.0.5",
|
||||
"redux": "^3.0.5",
|
||||
"redux-devtools": "^3.4.0",
|
||||
"redux-devtools-chart-monitor": "^1.6.1",
|
||||
"redux-devtools-instrument": "^1.8.0",
|
||||
"redux-devtools-log-monitor": "^1.3.0",
|
||||
"redux-devtools-test-generator": "^0.5.1",
|
||||
"redux-persist": "^4.8.0",
|
||||
"redux-slider-monitor": "^2.0.0-0",
|
||||
"remotedev-inspector-monitor": "^0.11.0",
|
||||
"socketcluster-client": "^5.5.0",
|
||||
"styled-components": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.4.0"
|
||||
}
|
||||
}
|
110
packages/redux-devtools-app/src/app/actions/index.js
Normal file
110
packages/redux-devtools-app/src/app/actions/index.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
import {
|
||||
CHANGE_SECTION, CHANGE_THEME, SELECT_INSTANCE, SELECT_MONITOR, UPDATE_MONITOR_STATE,
|
||||
LIFTED_ACTION, MONITOR_ACTION, EXPORT, TOGGLE_SYNC, TOGGLE_SLIDER, TOGGLE_DISPATCHER,
|
||||
TOGGLE_PERSIST, GET_REPORT_REQUEST, SHOW_NOTIFICATION, CLEAR_NOTIFICATION
|
||||
} from '../constants/actionTypes';
|
||||
import { RECONNECT } from '../constants/socketActionTypes';
|
||||
|
||||
let monitorReducer;
|
||||
let monitorProps = {};
|
||||
|
||||
export function changeSection(section) {
|
||||
return { type: CHANGE_SECTION, section };
|
||||
}
|
||||
|
||||
export function changeTheme(data) {
|
||||
return { type: CHANGE_THEME, ...data.formData };
|
||||
}
|
||||
|
||||
export function liftedDispatch(action) {
|
||||
if (action.type[0] === '@') {
|
||||
if (action.type === '@@INIT_MONITOR') {
|
||||
monitorReducer = action.update;
|
||||
monitorProps = action.monitorProps;
|
||||
}
|
||||
return { type: MONITOR_ACTION, action, monitorReducer, monitorProps };
|
||||
}
|
||||
return { type: LIFTED_ACTION, message: 'DISPATCH', action };
|
||||
}
|
||||
|
||||
export function selectInstance(selected) {
|
||||
return { type: SELECT_INSTANCE, selected };
|
||||
}
|
||||
|
||||
export function selectMonitor(monitor) {
|
||||
return { type: SELECT_MONITOR, monitor };
|
||||
}
|
||||
|
||||
export function selectMonitorWithState(value, monitorState) {
|
||||
return { type: SELECT_MONITOR, monitor: value, monitorState };
|
||||
}
|
||||
|
||||
export function selectMonitorTab(subTabName) {
|
||||
return { type: UPDATE_MONITOR_STATE, nextState: { subTabName } };
|
||||
}
|
||||
|
||||
export function updateMonitorState(nextState) {
|
||||
return { type: UPDATE_MONITOR_STATE, nextState };
|
||||
}
|
||||
|
||||
export function importState(state, preloadedState) {
|
||||
return { type: LIFTED_ACTION, message: 'IMPORT', state, preloadedState };
|
||||
}
|
||||
|
||||
export function exportState() {
|
||||
return { type: EXPORT };
|
||||
}
|
||||
|
||||
export function lockChanges(status) {
|
||||
return {
|
||||
type: LIFTED_ACTION,
|
||||
message: 'DISPATCH',
|
||||
action: { type: 'LOCK_CHANGES', status },
|
||||
toAll: true
|
||||
};
|
||||
}
|
||||
|
||||
export function pauseRecording(status) {
|
||||
return {
|
||||
type: LIFTED_ACTION,
|
||||
message: 'DISPATCH',
|
||||
action: { type: 'PAUSE_RECORDING', status },
|
||||
toAll: true
|
||||
};
|
||||
}
|
||||
|
||||
export function dispatchRemotely(action) {
|
||||
return { type: LIFTED_ACTION, message: 'ACTION', action };
|
||||
}
|
||||
|
||||
export function togglePersist() {
|
||||
return { type: TOGGLE_PERSIST };
|
||||
}
|
||||
|
||||
export function toggleSync() {
|
||||
return { type: TOGGLE_SYNC };
|
||||
}
|
||||
|
||||
export function toggleSlider() {
|
||||
return { type: TOGGLE_SLIDER };
|
||||
}
|
||||
|
||||
export function toggleDispatcher() {
|
||||
return { type: TOGGLE_DISPATCHER };
|
||||
}
|
||||
|
||||
export function saveSocketSettings(options) {
|
||||
return { type: RECONNECT, options };
|
||||
}
|
||||
|
||||
export function showNotification(message) {
|
||||
return { type: SHOW_NOTIFICATION, notification: { type: 'error', message } };
|
||||
}
|
||||
|
||||
export function clearNotification() {
|
||||
return { type: CLEAR_NOTIFICATION };
|
||||
}
|
||||
|
||||
export function getReport(report) {
|
||||
return { type: GET_REPORT_REQUEST, report };
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Toolbar, Divider, Spacer } from 'devui';
|
||||
import SaveIcon from 'react-icons/lib/md/save';
|
||||
import ExportButton from './buttons/ExportButton';
|
||||
import ImportButton from './buttons/ImportButton';
|
||||
import PrintButton from './buttons/PrintButton';
|
||||
import DispatcherButton from './buttons/DispatcherButton';
|
||||
import SliderButton from './buttons/SliderButton';
|
||||
import MonitorSelector from './MonitorSelector';
|
||||
|
||||
export default class BottomButtons extends Component {
|
||||
static propTypes = {
|
||||
dispatcherIsOpen: PropTypes.bool,
|
||||
sliderIsOpen: PropTypes.bool,
|
||||
options: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return nextProps.dispatcherIsOpen !== this.props.dispatcherIsOpen
|
||||
|| nextProps.sliderIsOpen !== this.props.sliderIsOpen
|
||||
|| nextProps.options !== this.props.options;
|
||||
}
|
||||
|
||||
render() {
|
||||
const features = this.props.options.features;
|
||||
return (
|
||||
<Toolbar borderPosition="top">
|
||||
{features.export &&
|
||||
<Button
|
||||
title="Save a report"
|
||||
tooltipPosition="top-right"
|
||||
>
|
||||
<SaveIcon />
|
||||
</Button>
|
||||
}
|
||||
{features.export &&
|
||||
<ExportButton />
|
||||
}
|
||||
{features.import &&
|
||||
<ImportButton />
|
||||
}
|
||||
<PrintButton />
|
||||
<Divider />
|
||||
<MonitorSelector />
|
||||
<Divider />
|
||||
{features.jump &&
|
||||
<SliderButton isOpen={this.props.sliderIsOpen} />
|
||||
}
|
||||
{features.dispatch &&
|
||||
<DispatcherButton dispatcherIsOpen={this.props.dispatcherIsOpen} />
|
||||
}
|
||||
</Toolbar>
|
||||
);
|
||||
}
|
||||
}
|
78
packages/redux-devtools-app/src/app/components/Header.js
Normal file
78
packages/redux-devtools-app/src/app/components/Header.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Tabs, Toolbar, Button, Divider, Spacer } from 'devui';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import DocsIcon from 'react-icons/lib/go/book';
|
||||
import FeedBackIcon from 'react-icons/lib/io/android-textsms';
|
||||
import TwitterIcon from 'react-icons/lib/ti/social-twitter';
|
||||
import SupportIcon from 'react-icons/lib/ti/heart-full-outline';
|
||||
import { changeSection } from '../actions';
|
||||
|
||||
const tabs = [
|
||||
{ name: 'Actions' },
|
||||
{ name: 'Reports' },
|
||||
{ name: 'Settings' }
|
||||
];
|
||||
|
||||
class Header extends Component {
|
||||
static propTypes = {
|
||||
section: PropTypes.string.isRequired,
|
||||
changeSection: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
openLink = url => () => {
|
||||
window.open(url);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Toolbar compact noBorder borderPosition="bottom">
|
||||
<Tabs
|
||||
main
|
||||
collapsible
|
||||
tabs={tabs}
|
||||
onClick={this.props.changeSection}
|
||||
selected={this.props.section || 'Actions'}
|
||||
/>
|
||||
<Divider />
|
||||
<Button
|
||||
title="Documentation"
|
||||
tooltipPosition="bottom"
|
||||
onClick={this.openLink('http://extension.remotedev.io')}
|
||||
>
|
||||
<DocsIcon />
|
||||
</Button>
|
||||
<Button
|
||||
title="Feedback"
|
||||
tooltipPosition="bottom"
|
||||
onClick={this.openLink('http://extension.remotedev.io/docs/Feedback.html')}
|
||||
>
|
||||
<FeedBackIcon />
|
||||
</Button>
|
||||
<Button
|
||||
title="Follow us"
|
||||
tooltipPosition="bottom"
|
||||
onClick={this.openLink('https://twitter.com/RemoteDev')}
|
||||
>
|
||||
<TwitterIcon />
|
||||
</Button>
|
||||
<Button
|
||||
title="Support us"
|
||||
tooltipPosition="bottom-left"
|
||||
onClick={this.openLink('https://opencollective.com/redux-devtools-extension')}
|
||||
>
|
||||
<SupportIcon />
|
||||
</Button>
|
||||
</Toolbar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
changeSection: bindActionCreators(changeSection, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(Header);
|
|
@ -0,0 +1,47 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Select } from 'devui';
|
||||
import { selectInstance } from '../actions';
|
||||
|
||||
class InstanceSelector extends Component {
|
||||
static propTypes = {
|
||||
selected: PropTypes.string,
|
||||
instances: PropTypes.object.isRequired,
|
||||
onSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
this.select = [{ value: '', label: 'Autoselect instances' }];
|
||||
const instances = this.props.instances;
|
||||
let name;
|
||||
Object.keys(instances).forEach(key => {
|
||||
name = instances[key].name;
|
||||
if (name !== undefined) this.select.push({ value: key, label: instances[key].name });
|
||||
});
|
||||
|
||||
return (
|
||||
<Select
|
||||
options={this.select}
|
||||
onChange={this.props.onSelect}
|
||||
value={this.props.selected || ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
selected: state.instances.selected,
|
||||
instances: state.instances.options
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
onSelect: bindActionCreators(selectInstance, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(InstanceSelector);
|
|
@ -0,0 +1,45 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Tabs } from 'devui';
|
||||
import { monitors } from '../utils/getMonitor';
|
||||
import { selectMonitor } from '../actions';
|
||||
|
||||
class MonitorSelector extends Component {
|
||||
static propTypes = {
|
||||
selected: PropTypes.string,
|
||||
selectMonitor: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.selected !== this.props.selected;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Tabs
|
||||
main
|
||||
collapsible
|
||||
position="center"
|
||||
tabs={monitors}
|
||||
onClick={this.props.selectMonitor}
|
||||
selected={this.props.selected || 'InspectorMonitor'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
selected: state.monitor.selected
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
selectMonitor: bindActionCreators(selectMonitor, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MonitorSelector);
|
|
@ -0,0 +1,126 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Container, Form, Button } from 'devui';
|
||||
import { saveSocketSettings } from '../../actions';
|
||||
|
||||
const defaultSchema = {
|
||||
type: 'object',
|
||||
required: [],
|
||||
properties: {
|
||||
type: {
|
||||
title: 'Connection settings (for getting reports and remote debugging)',
|
||||
type: 'string',
|
||||
enum: ['disabled', 'remotedev', 'custom'],
|
||||
enumNames: ['no remote connection', 'connect via remotedev.io', 'use local (custom) server']
|
||||
},
|
||||
hostname: {
|
||||
type: 'string'
|
||||
},
|
||||
port: {
|
||||
type: 'number'
|
||||
},
|
||||
secure: {
|
||||
type: 'boolean'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const uiSchema = {
|
||||
type: {
|
||||
'ui:widget': 'radio'
|
||||
}
|
||||
};
|
||||
|
||||
class Connection extends Component {
|
||||
static propTypes = {
|
||||
saveSettings: PropTypes.func.isRequired,
|
||||
options: PropTypes.object.isRequired,
|
||||
type: PropTypes.string
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = this.setFormData(props.type);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return this.state !== nextState;
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.props.options !== nextProps.options) {
|
||||
this.setState({ formData: { ...nextProps.options, type: nextProps.type } });
|
||||
}
|
||||
}
|
||||
|
||||
handleSave = data => {
|
||||
this.props.saveSettings(data.formData);
|
||||
this.setState({ changed: false });
|
||||
};
|
||||
|
||||
setFormData = (type, changed) => {
|
||||
let schema;
|
||||
if (type !== 'custom') {
|
||||
schema = {
|
||||
type: 'object',
|
||||
properties: { type: defaultSchema.properties.type }
|
||||
};
|
||||
} else {
|
||||
schema = defaultSchema;
|
||||
}
|
||||
return {
|
||||
formData: {
|
||||
type,
|
||||
...this.props.options
|
||||
},
|
||||
type,
|
||||
schema,
|
||||
changed
|
||||
};
|
||||
};
|
||||
|
||||
handleChange = data => {
|
||||
const formData = data.formData;
|
||||
const type = formData.type;
|
||||
if (type !== this.state.type) {
|
||||
this.setState(this.setFormData(type, true));
|
||||
} else if (!this.state.changed) {
|
||||
this.setState({ changed: true, formData });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const type = this.state.type || 'disabled';
|
||||
const changed = this.state.changed;
|
||||
const disabled = type === 'disabled';
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Form
|
||||
primaryButton={changed}
|
||||
noSubmit={disabled && !changed}
|
||||
submitText={disabled ? 'Disconnect' : 'Connect'}
|
||||
formData={this.state.formData}
|
||||
schema={this.state.schema}
|
||||
uiSchema={uiSchema}
|
||||
onChange={this.handleChange}
|
||||
onSubmit={this.handleSave}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return state.connection;
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
saveSettings: bindActionCreators(saveSocketSettings, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Connection);
|
|
@ -0,0 +1,64 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Container, Form, Button } from 'devui';
|
||||
import { listSchemes, listThemes } from 'devui/lib/utils/theme';
|
||||
import { changeTheme } from '../../actions';
|
||||
|
||||
class Themes extends Component {
|
||||
static propTypes = {
|
||||
changeTheme: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const theme = this.props.theme;
|
||||
const formData = {
|
||||
theme: theme.theme,
|
||||
scheme: theme.scheme,
|
||||
dark: !theme.light
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Form
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
theme: {
|
||||
type: 'string',
|
||||
enum: listThemes(),
|
||||
},
|
||||
scheme: {
|
||||
title: 'color scheme',
|
||||
type: 'string',
|
||||
enum: listSchemes(),
|
||||
},
|
||||
dark: {
|
||||
type: 'boolean'
|
||||
}
|
||||
}
|
||||
}}
|
||||
formData={formData}
|
||||
noSubmit
|
||||
onChange={this.props.changeTheme}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
theme: state.theme
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
changeTheme: bindActionCreators(changeTheme, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Themes);
|
|
@ -0,0 +1,33 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Tabs } from 'devui';
|
||||
import Connection from './Connection';
|
||||
import Themes from './Themes';
|
||||
|
||||
class Settings extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.tabs = [
|
||||
{ name: 'Connection', component: Connection },
|
||||
{ name: 'Themes', component: Themes }
|
||||
];
|
||||
this.state = { selected: 'Connection' };
|
||||
}
|
||||
|
||||
handleSelect = selected => {
|
||||
this.setState({ selected });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Tabs
|
||||
toRight
|
||||
tabs={this.tabs}
|
||||
selected={this.state.selected}
|
||||
onClick={this.handleSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Settings;
|
102
packages/redux-devtools-app/src/app/components/TopButtons.js
Normal file
102
packages/redux-devtools-app/src/app/components/TopButtons.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ActionCreators } from 'redux-devtools-instrument';
|
||||
import { Button, Toolbar, Divider, Spacer } from 'devui';
|
||||
import RecordButton from './buttons/RecordButton';
|
||||
import PersistButton from './buttons/PersistButton';
|
||||
import LockButton from './buttons/LockButton';
|
||||
import InstanceSelector from './InstanceSelector';
|
||||
import SyncButton from './buttons/SyncButton';
|
||||
|
||||
const { reset, rollback, commit, sweep } = ActionCreators;
|
||||
|
||||
export default class TopButtons extends Component {
|
||||
static propTypes = {
|
||||
// shouldSync: PropTypes.bool,
|
||||
liftedState: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
options: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.options !== this.props.options
|
||||
|| nextProps.liftedState !== this.props.liftedState;
|
||||
}
|
||||
|
||||
handleRollback = () => {
|
||||
this.props.dispatch(rollback());
|
||||
};
|
||||
|
||||
handleSweep = () => {
|
||||
this.props.dispatch(sweep());
|
||||
};
|
||||
|
||||
handleCommit = () => {
|
||||
this.props.dispatch(commit());
|
||||
};
|
||||
|
||||
handleReset = () => {
|
||||
this.props.dispatch(reset());
|
||||
};
|
||||
|
||||
render() {
|
||||
const options = this.props.options;
|
||||
const features = options.features;
|
||||
const { computedStates, skippedActionIds, isPaused, isLocked } = this.props.liftedState;
|
||||
const noStates = computedStates.length < 2;
|
||||
|
||||
return (
|
||||
<Toolbar borderPosition="bottom">
|
||||
{features.pause &&
|
||||
<RecordButton paused={isPaused} />
|
||||
}
|
||||
{features.persist &&
|
||||
<PersistButton />
|
||||
}
|
||||
{features.lock &&
|
||||
<LockButton
|
||||
locked={isLocked}
|
||||
disabled={options.lib !== 'redux'}
|
||||
/>
|
||||
}
|
||||
<Divider />
|
||||
<Button
|
||||
title="Reset to the state you created the store with"
|
||||
tooltipPosition="bottom"
|
||||
onClick={this.handleReset}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
<Button
|
||||
title="Roll back to the last committed state"
|
||||
tooltipPosition="bottom"
|
||||
onClick={this.handleRollback}
|
||||
disabled={noStates}
|
||||
>
|
||||
Revert
|
||||
</Button>
|
||||
<Button
|
||||
title="Remove all currently disabled actions from the log"
|
||||
tooltipPosition="bottom"
|
||||
onClick={this.handleSweep}
|
||||
disabled={skippedActionIds.length === 0}
|
||||
>
|
||||
Sweep
|
||||
</Button>
|
||||
<Button
|
||||
title="Remove all actions from the log,\a and make the current state your initial state"
|
||||
tooltipPosition="bottom"
|
||||
onClick={this.handleCommit}
|
||||
disabled={noStates}
|
||||
>
|
||||
Commit
|
||||
</Button>
|
||||
<Divider />
|
||||
<InstanceSelector />
|
||||
{features.sync &&
|
||||
<SyncButton />
|
||||
}
|
||||
</Toolbar>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'devui';
|
||||
import DispatchIcon from 'react-icons/lib/fa/terminal';
|
||||
import { toggleDispatcher } from '../../actions';
|
||||
|
||||
class DispatcherButton extends Component {
|
||||
static propTypes = {
|
||||
dispatcherIsOpen: PropTypes.bool,
|
||||
toggleDispatcher: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.dispatcherIsOpen !== this.props.dispatcherIsOpen;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
mark={this.props.dispatcherIsOpen && 'base0D'}
|
||||
title={this.props.dispatcherIsOpen ? 'Hide dispatcher' : 'Show dispatcher'}
|
||||
onClick={this.props.toggleDispatcher}
|
||||
tooltipPosition="top-left"
|
||||
>
|
||||
<DispatchIcon />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
toggleDispatcher: bindActionCreators(toggleDispatcher, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(DispatcherButton);
|
|
@ -0,0 +1,37 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'devui';
|
||||
import { stringify } from 'jsan';
|
||||
import DownloadIcon from 'react-icons/lib/ti/download';
|
||||
import { exportState } from '../../actions';
|
||||
|
||||
class ExportButton extends Component {
|
||||
static propTypes = {
|
||||
exportState: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
title="Export to a file"
|
||||
onClick={this.props.exportState}
|
||||
>
|
||||
<DownloadIcon />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
exportState: bindActionCreators(exportState, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ExportButton);
|
|
@ -0,0 +1,65 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'devui';
|
||||
import UploadIcon from 'react-icons/lib/ti/upload';
|
||||
import { importState } from '../../actions';
|
||||
|
||||
class ImportButton extends Component {
|
||||
static propTypes = {
|
||||
importState: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.handleImport = this.handleImport.bind(this);
|
||||
this.handleImportFile = this.handleImportFile.bind(this);
|
||||
this.mapRef = this.mapRef.bind(this);
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
mapRef(node) {
|
||||
this.fileInput = node;
|
||||
}
|
||||
|
||||
handleImport() {
|
||||
this.fileInput.click();
|
||||
}
|
||||
|
||||
handleImportFile(e) {
|
||||
const file = e.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
this.props.importState(reader.result);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
e.target.value = ''; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
title="Import from a file"
|
||||
onClick={this.handleImport}
|
||||
>
|
||||
<UploadIcon />
|
||||
<input
|
||||
type="file" ref={this.mapRef} style={{ display: 'none' }}
|
||||
onChange={this.handleImportFile}
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
importState: bindActionCreators(importState, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ImportButton);
|
|
@ -0,0 +1,40 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'devui';
|
||||
import LockIcon from 'react-icons/lib/io/ios-locked';
|
||||
import { lockChanges } from '../../actions';
|
||||
|
||||
class LockButton extends Component {
|
||||
static propTypes = {
|
||||
locked: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
lockChanges: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.locked !== this.props.locked;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
tooltipPosition="bottom"
|
||||
disabled={this.props.disabled}
|
||||
mark={this.props.locked && 'base0D'}
|
||||
title={this.props.locked ? 'Unlock changes' : 'Lock changes'}
|
||||
onClick={this.props.lockChanges}
|
||||
>
|
||||
<LockIcon />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch, ownProps) {
|
||||
return {
|
||||
lockChanges: () => dispatch(lockChanges(!ownProps.locked))
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(LockButton);
|
|
@ -0,0 +1,48 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Button } from 'devui';
|
||||
import PersistIcon from 'react-icons/lib/fa/thumb-tack';
|
||||
import { togglePersist } from '../../actions';
|
||||
|
||||
class LockButton extends Component {
|
||||
static propTypes = {
|
||||
persisted: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
onClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.persisted !== this.props.persisted;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
toolbar
|
||||
tooltipPosition="bottom"
|
||||
disabled={this.props.disabled}
|
||||
mark={this.props.persisted && 'base0D'}
|
||||
title={this.props.persisted ? 'Persist state history' : 'Disable state persisting'}
|
||||
onClick={this.props.onClick}
|
||||
>
|
||||
<PersistIcon />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
persisted: state.instances.persisted
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
onClick: bindActionCreators(togglePersist, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(LockButton);
|
|
@ -0,0 +1,40 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button } from 'devui';
|
||||
import PrintIcon from 'react-icons/lib/md/print';
|
||||
|
||||
export default class PrintButton extends Component {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
handlePrint() {
|
||||
const d3svg = document.getElementById('d3svg');
|
||||
if (!d3svg) {
|
||||
window.print();
|
||||
return;
|
||||
}
|
||||
|
||||
const initHeight = d3svg.style.height;
|
||||
const initWidth = d3svg.style.width;
|
||||
const box = d3svg.getBBox();
|
||||
d3svg.style.height = box.height;
|
||||
d3svg.style.width = box.width;
|
||||
|
||||
const g = d3svg.firstChild;
|
||||
const initTransform = g.getAttribute('transform');
|
||||
g.setAttribute('transform', initTransform.replace(/.+scale\(/, 'translate(57, 10) scale('));
|
||||
|
||||
window.print();
|
||||
|
||||
d3svg.style.height = initHeight;
|
||||
d3svg.style.width = initWidth;
|
||||
g.setAttribute('transform', initTransform);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button title="Print" onClick={this.handlePrint}><PrintIcon /></Button>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'devui';
|
||||
import RecordIcon from 'react-icons/lib/md/fiber-manual-record';
|
||||
import { pauseRecording } from '../../actions';
|
||||
|
||||
class RecordButton extends Component {
|
||||
static propTypes = {
|
||||
paused: PropTypes.bool,
|
||||
pauseRecording: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.paused !== this.props.paused;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
tooltipPosition="bottom-right"
|
||||
mark={!this.props.paused && 'base08'}
|
||||
title={this.props.paused ? 'Start recording' : 'Pause recording'}
|
||||
onClick={this.props.pauseRecording}
|
||||
>
|
||||
<RecordIcon />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch, ownProps) {
|
||||
return {
|
||||
pauseRecording: () => dispatch(pauseRecording(!ownProps.paused))
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(RecordButton);
|
|
@ -0,0 +1,39 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'devui';
|
||||
import HistoryIcon from 'react-icons/lib/md/av-timer';
|
||||
import { toggleSlider } from '../../actions';
|
||||
|
||||
class SliderButton extends Component {
|
||||
static propTypes = {
|
||||
isOpen: PropTypes.bool,
|
||||
toggleSlider: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.isOpen !== this.props.isOpen;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
mark={this.props.isOpen && 'base0D'}
|
||||
title={this.props.isOpen ? 'Hide slider' : 'Show slider'}
|
||||
tooltipPosition="top-left"
|
||||
onClick={this.props.toggleSlider}
|
||||
>
|
||||
<HistoryIcon />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
toggleSlider: bindActionCreators(toggleSlider, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(SliderButton);
|
|
@ -0,0 +1,45 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'devui';
|
||||
import SyncIcon from 'react-icons/lib/ti/arrow-sync';
|
||||
import { toggleSync } from '../../actions';
|
||||
|
||||
class SyncButton extends Component {
|
||||
static propTypes = {
|
||||
sync: PropTypes.bool,
|
||||
onClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.sync !== this.props.sync;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
title="Sync actions"
|
||||
tooltipPosition="bottom-left"
|
||||
onClick={this.props.onClick}
|
||||
mark={this.props.sync && 'base0B'}
|
||||
>
|
||||
<SyncIcon />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
sync: state.instances.sync
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
onClick: bindActionCreators(toggleSync, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SyncButton);
|
24
packages/redux-devtools-app/src/app/constants/actionTypes.js
Normal file
24
packages/redux-devtools-app/src/app/constants/actionTypes.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
export const CHANGE_SECTION = 'main/CHANGE_SECTION';
|
||||
export const CHANGE_THEME = 'main/CHANGE_THEME';
|
||||
|
||||
export const UPDATE_STATE = 'devTools/UPDATE_STATE';
|
||||
export const SET_STATE = 'devTools/SET_STATE';
|
||||
export const SELECT_INSTANCE = 'devTools/SELECT_INSTANCE';
|
||||
export const REMOVE_INSTANCE = 'devTools/REMOVE_INSTANCE';
|
||||
export const LIFTED_ACTION = 'devTools/LIFTED_ACTION';
|
||||
export const MONITOR_ACTION = 'devTools/MONITOR_ACTION';
|
||||
export const TOGGLE_SYNC = 'devTools/TOGGLE_SYNC';
|
||||
export const TOGGLE_PERSIST = 'devTools/TOGGLE_PERSIST';
|
||||
export const SELECT_MONITOR = 'devTools/SELECT_MONITOR';
|
||||
export const UPDATE_MONITOR_STATE = 'devTools/UPDATE_MONITOR_STATE';
|
||||
export const TOGGLE_SLIDER = 'devTools/TOGGLE_SLIDER';
|
||||
export const TOGGLE_DISPATCHER = 'devTools/TOGGLE_DISPATCHER';
|
||||
export const EXPORT = 'devTools/EXPORT';
|
||||
export const SHOW_NOTIFICATION = 'devTools/SHOW_NOTIFICATION';
|
||||
export const CLEAR_NOTIFICATION = 'devTools/CLEAR_NOTIFICATION';
|
||||
|
||||
export const UPDATE_REPORTS = 'reports/UPDATE';
|
||||
export const GET_REPORT_REQUEST = 'reports/GET_REPORT_REQUEST';
|
||||
export const GET_REPORT_ERROR = 'reports/GET_REPORT_ERROR';
|
||||
export const GET_REPORT_SUCCESS = 'reports/GET_REPORT_SUCCESS';
|
||||
export const ERROR = 'ERROR';
|
|
@ -0,0 +1,2 @@
|
|||
export const DATA_TYPE_KEY = Symbol.for('__serializedType__');
|
||||
export const DATA_REF_KEY = Symbol.for('__serializedRef__');
|
|
@ -0,0 +1,19 @@
|
|||
import socketCluster from 'socketcluster-client';
|
||||
|
||||
export const {
|
||||
CLOSED, CONNECTING, OPEN, AUTHENTICATED, PENDING, UNAUTHENTICATED
|
||||
} = socketCluster.SCSocket;
|
||||
export const CONNECT_REQUEST = 'socket/CONNECT_REQUEST';
|
||||
export const CONNECT_SUCCESS = 'socket/CONNECT_SUCCESS';
|
||||
export const CONNECT_ERROR = 'socket/CONNECT_ERROR';
|
||||
export const RECONNECT = 'socket/RECONNECT';
|
||||
export const AUTH_REQUEST = 'socket/AUTH_REQUEST';
|
||||
export const AUTH_SUCCESS = 'socket/AUTH_SUCCESS';
|
||||
export const AUTH_ERROR = 'socket/AUTH_ERROR';
|
||||
export const DISCONNECTED = 'socket/DISCONNECTED';
|
||||
export const DEAUTHENTICATE = 'socket/DEAUTHENTICATE';
|
||||
export const SUBSCRIBE_REQUEST = 'socket/SUBSCRIBE_REQUEST';
|
||||
export const SUBSCRIBE_SUCCESS = 'socket/SUBSCRIBE_SUCCESS';
|
||||
export const SUBSCRIBE_ERROR = 'socket/SUBSCRIBE_ERROR';
|
||||
export const UNSUBSCRIBE = 'socket/UNSUBSCRIBE';
|
||||
export const EMIT = 'socket/EMIT';
|
|
@ -0,0 +1,12 @@
|
|||
const socketOptions = {
|
||||
hostname: 'remotedev.io',
|
||||
port: 443,
|
||||
protocol: 'https',
|
||||
autoReconnect: true,
|
||||
secure: true,
|
||||
autoReconnectOptions: {
|
||||
randomness: 30000
|
||||
}
|
||||
};
|
||||
|
||||
export default socketOptions;
|
80
packages/redux-devtools-app/src/app/containers/Actions.js
Normal file
80
packages/redux-devtools-app/src/app/containers/Actions.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Container } from 'devui';
|
||||
import SliderMonitor from './monitors/Slider';
|
||||
import { liftedDispatch as liftedDispatchAction, getReport } from '../actions';
|
||||
import { getActiveInstance } from '../reducers/instances';
|
||||
import DevTools from '../containers/DevTools';
|
||||
import Dispatcher from './monitors/Dispatcher';
|
||||
import TopButtons from '../components/TopButtons';
|
||||
import BottomButtons from '../components/BottomButtons';
|
||||
|
||||
class Actions extends Component {
|
||||
render() {
|
||||
const {
|
||||
monitor, dispatcherIsOpen, sliderIsOpen, options, liftedState, liftedDispatch
|
||||
} = this.props;
|
||||
return (
|
||||
<Container>
|
||||
<TopButtons
|
||||
dispatch={liftedDispatch}
|
||||
liftedState={liftedState}
|
||||
options={options}
|
||||
/>
|
||||
<DevTools
|
||||
monitor={monitor}
|
||||
liftedState={liftedState}
|
||||
monitorState={this.props.monitorState}
|
||||
dispatch={liftedDispatch}
|
||||
features={options.features}
|
||||
/>
|
||||
{sliderIsOpen && options.connectionId && options.features.jump &&
|
||||
<SliderMonitor liftedState={liftedState} dispatch={liftedDispatch} />
|
||||
}
|
||||
{dispatcherIsOpen && options.connectionId && options.features.dispatch &&
|
||||
<Dispatcher options={options} />
|
||||
}
|
||||
<BottomButtons
|
||||
dispatcherIsOpen={dispatcherIsOpen}
|
||||
sliderIsOpen={sliderIsOpen}
|
||||
options={options}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Actions.propTypes = {
|
||||
liftedDispatch: PropTypes.func.isRequired,
|
||||
liftedState: PropTypes.object.isRequired,
|
||||
monitorState: PropTypes.object,
|
||||
options: PropTypes.object.isRequired,
|
||||
monitor: PropTypes.string,
|
||||
dispatcherIsOpen: PropTypes.bool,
|
||||
sliderIsOpen: PropTypes.bool
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const instances = state.instances;
|
||||
const id = getActiveInstance(instances);
|
||||
return {
|
||||
liftedState: instances.states[id],
|
||||
monitorState: state.monitor.monitorState,
|
||||
options: instances.options[id],
|
||||
monitor: state.monitor.selected,
|
||||
dispatcherIsOpen: state.monitor.dispatcherIsOpen,
|
||||
sliderIsOpen: state.monitor.sliderIsOpen,
|
||||
reports: state.reports.data
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
liftedDispatch: bindActionCreators(liftedDispatchAction, dispatch),
|
||||
getReport: bindActionCreators(getReport, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Actions);
|
58
packages/redux-devtools-app/src/app/containers/App.js
Normal file
58
packages/redux-devtools-app/src/app/containers/App.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Container, Notification } from 'devui';
|
||||
import { clearNotification } from '../actions';
|
||||
import Header from '../components/Header';
|
||||
import Actions from '../containers/Actions';
|
||||
import Settings from '../components/Settings';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
const { section, theme, notification } = this.props;
|
||||
let body;
|
||||
switch (section) {
|
||||
case 'Settings': body = <Settings />; break;
|
||||
default: body = <Actions />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container themeData={theme}>
|
||||
<Header section={section} />
|
||||
{body}
|
||||
{notification &&
|
||||
<Notification type={notification.type} onClose={this.props.clearNotification}>
|
||||
{notification.message}
|
||||
</Notification>
|
||||
}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
section: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
notification: PropTypes.shape({
|
||||
message: PropTypes.string,
|
||||
type: PropTypes.string
|
||||
}),
|
||||
clearNotification: PropTypes.func
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
section: state.section,
|
||||
theme: state.theme,
|
||||
notification: state.notification
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
clearNotification: bindActionCreators(clearNotification, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
88
packages/redux-devtools-app/src/app/containers/DevTools.js
Normal file
88
packages/redux-devtools-app/src/app/containers/DevTools.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
import React, { Component, PropTypes, createElement } from 'react';
|
||||
import { withTheme } from 'styled-components';
|
||||
import getMonitor from '../utils/getMonitor';
|
||||
|
||||
class DevTools extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.getMonitor(props, props.monitorState);
|
||||
}
|
||||
|
||||
getMonitor(props, skipUpdate) {
|
||||
const monitorElement = getMonitor(props);
|
||||
this.monitorProps = monitorElement.props;
|
||||
this.Monitor = monitorElement.type;
|
||||
|
||||
const update = this.Monitor.update;
|
||||
if (update) {
|
||||
let newMonitorState;
|
||||
const monitorState = props.monitorState;
|
||||
if (skipUpdate || monitorState && monitorState.__overwritten__ === props.monitor) {
|
||||
newMonitorState = monitorState;
|
||||
} else {
|
||||
newMonitorState = update(this.monitorProps, undefined, {});
|
||||
if (newMonitorState !== monitorState) {
|
||||
this.preventRender = true;
|
||||
}
|
||||
}
|
||||
this.dispatch({
|
||||
type: '@@INIT_MONITOR',
|
||||
newMonitorState,
|
||||
update,
|
||||
monitorProps: this.monitorProps
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
if (nextProps.monitor !== this.props.monitor) this.getMonitor(nextProps);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (
|
||||
nextProps.monitor !== this.props.monitor ||
|
||||
nextProps.liftedState !== this.props.liftedState ||
|
||||
nextProps.monitorState !== this.props.liftedState ||
|
||||
nextProps.features !== this.props.features ||
|
||||
nextProps.theme.scheme !== this.props.theme.scheme
|
||||
);
|
||||
}
|
||||
|
||||
dispatch = action => {
|
||||
this.props.dispatch(action);
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.preventRender) {
|
||||
this.preventRender = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
const liftedState = {
|
||||
...this.props.liftedState,
|
||||
monitorState: this.props.monitorState
|
||||
};
|
||||
return (
|
||||
<div className={`monitor monitor-${this.props.monitor}`}>
|
||||
<this.Monitor
|
||||
{...liftedState}
|
||||
{...this.monitorProps}
|
||||
features={this.props.features}
|
||||
dispatch={this.dispatch}
|
||||
theme={this.props.theme}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DevTools.propTypes = {
|
||||
liftedState: PropTypes.object,
|
||||
monitorState: PropTypes.object,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
monitor: PropTypes.string,
|
||||
features: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default withTheme(DevTools);
|
|
@ -0,0 +1,55 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import ChartMonitor from 'redux-devtools-chart-monitor';
|
||||
import { selectMonitorWithState } from '../../actions';
|
||||
|
||||
export function getPath(obj, inspectedStatePath) {
|
||||
const parent = obj.parent;
|
||||
if (!parent) return;
|
||||
getPath(parent, inspectedStatePath);
|
||||
let name = obj.name;
|
||||
const item = name.match(/.+\[(\d+)]/);
|
||||
if (item) name = item[1];
|
||||
inspectedStatePath.push(name);
|
||||
}
|
||||
|
||||
class ChartMonitorWrapper extends Component {
|
||||
static update = ChartMonitor.update;
|
||||
|
||||
onClickText = (data) => {
|
||||
const inspectedStatePath = [];
|
||||
getPath(data, inspectedStatePath);
|
||||
this.props.selectMonitorWithState('InspectorMonitor', {
|
||||
inspectedStatePath,
|
||||
tabName: 'State',
|
||||
subTabName: data.children ? 'Chart' : 'Tree',
|
||||
selectedActionId: null,
|
||||
startActionId: null,
|
||||
inspectedActionPath: []
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ChartMonitor
|
||||
defaultIsVisible invertTheme
|
||||
onClickText={this.onClickText}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ChartMonitorWrapper.propTypes = {
|
||||
selectMonitorWithState: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
selectMonitorWithState: bindActionCreators(selectMonitorWithState, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ChartMonitorWrapper);
|
|
@ -0,0 +1,201 @@
|
|||
// Based on https://github.com/YoruNoHikage/redux-devtools-dispatch
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { Button, Select, Editor, Toolbar } from 'devui';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { dispatchRemotely } from '../../actions';
|
||||
|
||||
export const DispatcherContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
padding-top: 2px;
|
||||
background: ${props => props.theme.base01};
|
||||
`;
|
||||
|
||||
export const CodeContainer = styled.div`
|
||||
height: 75px;
|
||||
padding-right: 6px;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export const ActionContainer = styled.div`
|
||||
display: table;
|
||||
width: 100%;
|
||||
color: ${props => props.theme.base06};
|
||||
|
||||
> div {
|
||||
display: table-row;
|
||||
|
||||
> div:first-child {
|
||||
width: 1px;
|
||||
padding-left: 8px;
|
||||
display: table-cell;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
class Dispatcher extends Component {
|
||||
static propTypes = {
|
||||
options: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
state = {
|
||||
selected: 'default',
|
||||
customAction: this.props.options.lib === 'redux' ? '{\n type: \'\'\n}' : 'this.',
|
||||
args: [],
|
||||
rest: '[]',
|
||||
changed: false
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.state.selected !== 'default' && !nextProps.options.actionCreators) {
|
||||
this.setState({
|
||||
selected: 'default',
|
||||
args: []
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return nextState !== this.state ||
|
||||
nextProps.options.actionCreators !== this.props.options.actionCreators;
|
||||
}
|
||||
|
||||
selectActionCreator = selected => {
|
||||
if (selected === 'actions-help') {
|
||||
window.open('https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/' +
|
||||
'basics/Dispatcher.md');
|
||||
return;
|
||||
}
|
||||
|
||||
const args = [];
|
||||
if (selected !== 'default') {
|
||||
args.length = this.props.options.actionCreators[selected].args.length;
|
||||
}
|
||||
this.setState({ selected, args, rest: '[]', changed: false });
|
||||
};
|
||||
|
||||
handleArg = argIndex => value => {
|
||||
const args = [
|
||||
...this.state.args.slice(0, argIndex),
|
||||
value || undefined,
|
||||
...this.state.args.slice(argIndex + 1),
|
||||
];
|
||||
this.setState({ args, changed: true });
|
||||
};
|
||||
|
||||
handleRest = rest => {
|
||||
this.setState({ rest, changed: true });
|
||||
};
|
||||
|
||||
handleCustomAction = customAction => {
|
||||
this.setState({ customAction, changed: true });
|
||||
};
|
||||
|
||||
dispatchAction = () => {
|
||||
const { selected, customAction, args, rest } = this.state;
|
||||
|
||||
if (this.state.selected !== 'default') {
|
||||
// remove trailing `undefined` arguments
|
||||
let i = args.length - 1;
|
||||
while (i >= 0 && typeof args[i] === 'undefined') {
|
||||
args.pop(i); i--;
|
||||
}
|
||||
this.props.dispatch({
|
||||
name: this.props.options.actionCreators[selected].name,
|
||||
selected,
|
||||
args,
|
||||
rest
|
||||
});
|
||||
} else {
|
||||
this.props.dispatch(customAction);
|
||||
}
|
||||
this.setState({ changed: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
const actionCreators = this.props.options.actionCreators;
|
||||
let actionElement;
|
||||
|
||||
if (this.state.selected === 'default' || !actionCreators) {
|
||||
actionElement = (
|
||||
<CodeContainer>
|
||||
<Editor
|
||||
value={this.state.customAction}
|
||||
onChange={this.handleCustomAction}
|
||||
/>
|
||||
</CodeContainer>
|
||||
);
|
||||
} else {
|
||||
actionElement = (
|
||||
<ActionContainer>
|
||||
{actionCreators[this.state.selected].args.map((param, i) => (
|
||||
<div key={`${param}${i}`}>
|
||||
<div>{param}</div>
|
||||
<Editor
|
||||
lineNumbers={false}
|
||||
value={this.state.args[i]}
|
||||
onChange={this.handleArg(i)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div>
|
||||
<div>...rest</div>
|
||||
<Editor
|
||||
lineNumbers={false}
|
||||
value={this.state.rest}
|
||||
onChange={this.handleRest}
|
||||
/>
|
||||
</div>
|
||||
</ActionContainer>
|
||||
);
|
||||
}
|
||||
|
||||
let options = [{ value: 'default', label: 'Custom action' }];
|
||||
if (actionCreators && actionCreators.length > 0) {
|
||||
options = options.concat(actionCreators.map(({ name, func, args }, i) => ({
|
||||
value: i,
|
||||
label: `${name}(${args.join(', ')})`
|
||||
})));
|
||||
} else {
|
||||
options.push({ value: 'actions-help', label: 'Add your app built-in actions…' });
|
||||
}
|
||||
|
||||
return (
|
||||
<DispatcherContainer>
|
||||
{actionElement}
|
||||
<Toolbar>
|
||||
<Select
|
||||
openOuterUp
|
||||
onChange={this.selectActionCreator}
|
||||
value={this.state.selected || 'default'}
|
||||
options={options}
|
||||
/>
|
||||
<Button onClick={this.dispatchAction} primary={this.state.changed}>Dispatch</Button>
|
||||
</Toolbar>
|
||||
</DispatcherContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
dispatch: bindActionCreators(dispatchRemotely, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(Dispatcher);
|
|
@ -0,0 +1,109 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { withTheme } from 'styled-components';
|
||||
import { tree } from 'd3-state-visualizer';
|
||||
import { getPath } from '../ChartMonitorWrapper';
|
||||
import { updateMonitorState } from '../../../actions';
|
||||
|
||||
const style = {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
};
|
||||
|
||||
class ChartTab extends Component {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.createChart(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (
|
||||
this.props.theme.scheme !== nextProps.theme.scheme ||
|
||||
nextProps.theme.light !== this.props.theme.light
|
||||
) {
|
||||
this.node.innerHTML = '';
|
||||
this.createChart(nextProps);
|
||||
} else if (nextProps.data !== this.props.data) {
|
||||
this.renderChart(nextProps.data);
|
||||
}
|
||||
}
|
||||
|
||||
getRef = node => {
|
||||
this.node = node;
|
||||
};
|
||||
|
||||
createChart(props) {
|
||||
this.renderChart = tree(this.node, this.getChartTheme(props.theme));
|
||||
this.renderChart(props.data);
|
||||
}
|
||||
|
||||
getChartTheme(theme) {
|
||||
return {
|
||||
heightBetweenNodesCoeff: 1,
|
||||
widthBetweenNodesCoeff: 1.3,
|
||||
tooltipOptions: {
|
||||
style: {
|
||||
color: theme.base06,
|
||||
'background-color': theme.base01,
|
||||
opacity: '0.9',
|
||||
'border-radius': '5px',
|
||||
padding: '5px'
|
||||
},
|
||||
offset: { left: 30, top: 10 },
|
||||
indentationSize: 2
|
||||
},
|
||||
style: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
node: {
|
||||
colors: {
|
||||
default: theme.base0B,
|
||||
collapsed: theme.base0B,
|
||||
parent: theme.base0E
|
||||
},
|
||||
radius: 7
|
||||
},
|
||||
text: {
|
||||
colors: {
|
||||
default: theme.base0D,
|
||||
hover: theme.base06
|
||||
}
|
||||
}
|
||||
},
|
||||
onClickText: this.onClickText
|
||||
};
|
||||
}
|
||||
|
||||
onClickText = (data) => {
|
||||
const inspectedStatePath = [];
|
||||
getPath(data, inspectedStatePath);
|
||||
this.props.updateMonitorState({
|
||||
inspectedStatePath,
|
||||
subTabName: data.children ? 'Chart' : 'Tree'
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return <div style={style} ref={this.getRef} />;
|
||||
}
|
||||
}
|
||||
|
||||
ChartTab.propTypes = {
|
||||
data: PropTypes.object,
|
||||
updateMonitorState: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
updateMonitorState: bindActionCreators(updateMonitorState, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
const ConnectedChartTab = connect(null, mapDispatchToProps)(ChartTab);
|
||||
export default withTheme(ConnectedChartTab);
|
|
@ -0,0 +1,29 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Editor } from 'devui';
|
||||
import stringify from 'javascript-stringify';
|
||||
|
||||
export default class RawTab extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.stringifyData(props);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.data !== this.value;
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this.stringifyData(nextProps);
|
||||
}
|
||||
|
||||
stringifyData(props) {
|
||||
this.value = stringify(props.data, null, 2);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Editor value={this.value} />
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Tabs } from 'devui';
|
||||
import StateTree from 'remotedev-inspector-monitor/lib/tabs/StateTab';
|
||||
import ActionTree from 'remotedev-inspector-monitor/lib/tabs/ActionTab';
|
||||
import DiffTree from 'remotedev-inspector-monitor/lib/tabs/DiffTab';
|
||||
import { selectMonitorTab } from '../../../actions';
|
||||
import RawTab from './RawTab';
|
||||
import ChartTab from './ChartTab';
|
||||
import VisualDiffTab from './VisualDiffTab';
|
||||
|
||||
class SubTabs extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.updateTabs(props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.parentTab !== this.props.parentTab) {
|
||||
this.updateTabs(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
selector = () => {
|
||||
switch (this.props.parentTab) {
|
||||
case 'Action':
|
||||
return { data: this.props.action };
|
||||
case 'Diff':
|
||||
return { data: this.props.delta };
|
||||
default:
|
||||
return { data: this.props.nextState };
|
||||
}
|
||||
};
|
||||
|
||||
updateTabs(props) {
|
||||
const parentTab = props.parentTab;
|
||||
|
||||
if (parentTab === 'Diff') {
|
||||
this.tabs = [
|
||||
{
|
||||
name: 'Tree',
|
||||
component: DiffTree,
|
||||
selector: () => this.props
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
component: VisualDiffTab,
|
||||
selector: this.selector
|
||||
}
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
this.tabs = [
|
||||
{
|
||||
name: 'Tree',
|
||||
component: parentTab === 'Action' ? ActionTree : StateTree,
|
||||
selector: () => this.props
|
||||
},
|
||||
{
|
||||
name: 'Chart',
|
||||
component: ChartTab,
|
||||
selector: this.selector
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
component: RawTab,
|
||||
selector: this.selector
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
let selected = this.props.selected;
|
||||
if (selected === 'Chart' && this.props.parentTab === 'Diff') selected = 'Tree';
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
tabs={this.tabs}
|
||||
selected={selected || 'Tree'}
|
||||
onClick={this.props.selectMonitorTab}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SubTabs.propTypes = {
|
||||
selected: PropTypes.string,
|
||||
parentTab: PropTypes.string,
|
||||
selectMonitorTab: PropTypes.func.isRequired,
|
||||
action: PropTypes.object,
|
||||
delta: PropTypes.object,
|
||||
nextState: PropTypes.object
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
parentTab: state.monitor.monitorState.tabName,
|
||||
selected: state.monitor.monitorState.subTabName
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
selectMonitorTab: bindActionCreators(selectMonitorTab, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SubTabs);
|
|
@ -0,0 +1,226 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { format } from 'jsondiffpatch/src/formatters/html';
|
||||
import styled from 'styled-components';
|
||||
import { effects } from 'devui';
|
||||
|
||||
export const StyledContainer = styled.div`
|
||||
.jsondiffpatch-delta {
|
||||
line-height: 14px;
|
||||
font-size: 12px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.jsondiffpatch-delta pre {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
padding: 2px 3px;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
color: ${props => props.theme.base07};
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
ul.jsondiffpatch-delta {
|
||||
list-style-type: none;
|
||||
padding: 0 0 0 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.jsondiffpatch-delta ul {
|
||||
list-style-type: none;
|
||||
padding: 0 0 0 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.jsondiffpatch-left-value, .jsondiffpatch-right-value {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.jsondiffpatch-modified .jsondiffpatch-right-value:before {
|
||||
vertical-align: top;
|
||||
padding: 2px;
|
||||
color: ${props => props.theme.base0E};
|
||||
content: ' => ';
|
||||
}
|
||||
|
||||
.jsondiffpatch-added .jsondiffpatch-value pre,
|
||||
.jsondiffpatch-modified .jsondiffpatch-right-value pre,
|
||||
.jsondiffpatch-textdiff-added {
|
||||
background: ${props => effects.color(props.theme.base0B, 'alpha', 0.2)};
|
||||
}
|
||||
|
||||
.jsondiffpatch-deleted pre,
|
||||
.jsondiffpatch-modified .jsondiffpatch-left-value pre,
|
||||
.jsondiffpatch-textdiff-deleted {
|
||||
background: ${props => effects.color(props.theme.base08, 'alpha', 0.2)};
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.jsondiffpatch-unchanged,
|
||||
.jsondiffpatch-movedestination {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.jsondiffpatch-unchanged,
|
||||
.jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||
transition: all 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.jsondiffpatch-unchanged-showing .jsondiffpatch-unchanged,
|
||||
.jsondiffpatch-unchanged-showing .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||
max-height: 100px;
|
||||
}
|
||||
|
||||
.jsondiffpatch-unchanged-hidden .jsondiffpatch-unchanged,
|
||||
.jsondiffpatch-unchanged-hidden .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.jsondiffpatch-unchanged-hiding .jsondiffpatch-movedestination > .jsondiffpatch-value,
|
||||
.jsondiffpatch-unchanged-hidden .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.jsondiffpatch-unchanged-visible .jsondiffpatch-unchanged,
|
||||
.jsondiffpatch-unchanged-visible .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||
max-height: 100px;
|
||||
}
|
||||
|
||||
.jsondiffpatch-unchanged-hiding .jsondiffpatch-unchanged,
|
||||
.jsondiffpatch-unchanged-hiding .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.jsondiffpatch-unchanged-showing .jsondiffpatch-arrow,
|
||||
.jsondiffpatch-unchanged-hiding .jsondiffpatch-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsondiffpatch-value {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.jsondiffpatch-property-name {
|
||||
display: inline-block;
|
||||
padding: 2px 0;
|
||||
padding-right: 5px;
|
||||
vertical-align: top;
|
||||
color: ${props => props.theme.base0D};
|
||||
}
|
||||
|
||||
.jsondiffpatch-property-name:after {
|
||||
content: ': ';
|
||||
color: ${props => props.theme.base07};
|
||||
}
|
||||
|
||||
.jsondiffpatch-child-node-type-array > .jsondiffpatch-property-name:after {
|
||||
content: ': [';
|
||||
}
|
||||
|
||||
.jsondiffpatch-child-node-type-array:after {
|
||||
content: '],';
|
||||
}
|
||||
|
||||
div.jsondiffpatch-child-node-type-array:before {
|
||||
content: '[';
|
||||
}
|
||||
|
||||
div.jsondiffpatch-child-node-type-array:after {
|
||||
content: ']';
|
||||
}
|
||||
|
||||
.jsondiffpatch-child-node-type-object > .jsondiffpatch-property-name:after {
|
||||
content: ': {';
|
||||
}
|
||||
|
||||
.jsondiffpatch-child-node-type-object:after {
|
||||
content: '},';
|
||||
}
|
||||
|
||||
div.jsondiffpatch-child-node-type-object:before {
|
||||
content: '{';
|
||||
}
|
||||
|
||||
div.jsondiffpatch-child-node-type-object:after {
|
||||
content: '}';
|
||||
}
|
||||
|
||||
.jsondiffpatch-value pre:after {
|
||||
color: ${props => props.theme.base07};
|
||||
content: ',';
|
||||
}
|
||||
|
||||
li:last-child > .jsondiffpatch-value pre:after,
|
||||
.jsondiffpatch-modified > .jsondiffpatch-left-value pre:after {
|
||||
content: '';
|
||||
}
|
||||
|
||||
.jsondiffpatch-modified .jsondiffpatch-value {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.jsondiffpatch-modified .jsondiffpatch-right-value {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.jsondiffpatch-moved .jsondiffpatch-value {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsondiffpatch-moved .jsondiffpatch-moved-destination {
|
||||
display: inline-block;
|
||||
background: ${props => props.theme.base0A};
|
||||
}
|
||||
|
||||
.jsondiffpatch-moved .jsondiffpatch-moved-destination:before {
|
||||
content: ' => ';
|
||||
}
|
||||
|
||||
ul.jsondiffpatch-textdiff {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.jsondiffpatch-textdiff-location {
|
||||
display: inline-block;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.jsondiffpatch-textdiff-line {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.jsondiffpatch-textdiff-line-number:after {
|
||||
content: ',';
|
||||
}
|
||||
|
||||
.jsondiffpatch-error {
|
||||
background: red;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
`;
|
||||
|
||||
export default class VisualDiffTab extends Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return this.props.data !== nextProps.data;
|
||||
}
|
||||
|
||||
render() {
|
||||
let __html;
|
||||
const data = this.props.data;
|
||||
if (data) {
|
||||
__html = format(data);
|
||||
}
|
||||
|
||||
return <StyledContainer dangerouslySetInnerHTML={{ __html }} />;
|
||||
}
|
||||
}
|
||||
|
||||
VisualDiffTab.propTypes = {
|
||||
data: PropTypes.object
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import InspectorMonitor from 'remotedev-inspector-monitor';
|
||||
import TestTab from 'redux-devtools-test-generator';
|
||||
import { DATA_TYPE_KEY } from '../../../constants/dataTypes';
|
||||
import SubTabs from './SubTabs';
|
||||
|
||||
const DEFAULT_TABS = [{
|
||||
name: 'Action',
|
||||
component: SubTabs
|
||||
}, {
|
||||
name: 'State',
|
||||
component: SubTabs
|
||||
}, {
|
||||
name: 'Diff',
|
||||
component: SubTabs
|
||||
}];
|
||||
|
||||
class InspectorWrapper extends Component {
|
||||
static update = InspectorMonitor.update;
|
||||
|
||||
render() {
|
||||
const { features, ...rest } = this.props;
|
||||
let tabs;
|
||||
if (features && features.test) {
|
||||
tabs = () => [...DEFAULT_TABS, { name: 'Test', component: TestTab }];
|
||||
} else {
|
||||
tabs = () => DEFAULT_TABS;
|
||||
}
|
||||
|
||||
return (
|
||||
<InspectorMonitor
|
||||
dataTypeKey={DATA_TYPE_KEY}
|
||||
shouldPersistState={false}
|
||||
invertTheme={false}
|
||||
tabs={tabs}
|
||||
hideActionButtons={!features.skip}
|
||||
hideMainButtons
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
InspectorWrapper.propTypes = {
|
||||
features: PropTypes.object
|
||||
};
|
||||
|
||||
export default InspectorWrapper;
|
|
@ -0,0 +1,39 @@
|
|||
import React, { Component, createElement } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled, { withTheme } from 'styled-components';
|
||||
import SliderMonitor from 'redux-slider-monitor';
|
||||
|
||||
const SliderWrapper = styled.div`
|
||||
border-color: ${props => props.theme.base02};
|
||||
border-style: solid;
|
||||
border-width: 1px 0;
|
||||
`;
|
||||
|
||||
class Slider extends Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (
|
||||
nextProps.liftedState !== this.props.liftedState ||
|
||||
nextProps.theme.scheme !== this.props.theme.scheme
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<SliderWrapper>
|
||||
<SliderMonitor
|
||||
{...this.props.liftedState}
|
||||
dispatch={this.props.dispatch}
|
||||
theme={this.props.theme}
|
||||
hideResetButton
|
||||
/>
|
||||
</SliderWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Slider.propTypes = {
|
||||
liftedState: PropTypes.object,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default withTheme(Slider);
|
40
packages/redux-devtools-app/src/app/index.js
Normal file
40
packages/redux-devtools-app/src/app/index.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import 'devui/lib/presets';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from './store/configureStore';
|
||||
import { CONNECT_REQUEST } from './constants/socketActionTypes';
|
||||
import App from './containers/App';
|
||||
|
||||
class Root extends Component {
|
||||
componentWillMount() {
|
||||
configureStore((store, preloadedState) => {
|
||||
this.store = store;
|
||||
store.dispatch({
|
||||
type: CONNECT_REQUEST,
|
||||
options: preloadedState.connection || this.props.socketOptions
|
||||
});
|
||||
this.forceUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.store) return null;
|
||||
return (
|
||||
<Provider store={this.store}>
|
||||
<App {...this.props} />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Root.propTypes = {
|
||||
socketOptions: PropTypes.shape({
|
||||
hostname: PropTypes.string,
|
||||
port: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
autoReconnect: PropTypes.bool,
|
||||
secure: PropTypes.bool
|
||||
})
|
||||
};
|
||||
|
||||
export default Root;
|
201
packages/redux-devtools-app/src/app/middlewares/api.js
Normal file
201
packages/redux-devtools-app/src/app/middlewares/api.js
Normal file
|
@ -0,0 +1,201 @@
|
|||
import socketCluster from 'socketcluster-client';
|
||||
import { stringify } from 'jsan';
|
||||
import socketOptions from '../constants/socketOptions';
|
||||
import * as actions from '../constants/socketActionTypes';
|
||||
import { getActiveInstance } from '../reducers/instances';
|
||||
import {
|
||||
UPDATE_STATE, REMOVE_INSTANCE, LIFTED_ACTION,
|
||||
UPDATE_REPORTS, GET_REPORT_REQUEST, GET_REPORT_ERROR, GET_REPORT_SUCCESS
|
||||
} from '../constants/actionTypes';
|
||||
import { showNotification, importState } from '../actions';
|
||||
import { nonReduxDispatch } from '../utils/monitorActions';
|
||||
|
||||
let socket;
|
||||
let store;
|
||||
|
||||
function emit({ message: type, id, instanceId, action, state }) {
|
||||
socket.emit(
|
||||
id ? 'sc-' + id : 'respond',
|
||||
{ type, action, state, instanceId }
|
||||
);
|
||||
}
|
||||
|
||||
function startMonitoring(channel) {
|
||||
if (channel !== store.getState().socket.baseChannel) return;
|
||||
store.dispatch({ type: actions.EMIT, message: 'START' });
|
||||
}
|
||||
|
||||
function dispatchRemoteAction({ message, action, state, toAll }) {
|
||||
const instances = store.getState().instances;
|
||||
const instanceId = getActiveInstance(instances);
|
||||
const id = !toAll && instances.options[instanceId].connectionId;
|
||||
store.dispatch({
|
||||
type: actions.EMIT,
|
||||
message,
|
||||
action,
|
||||
state: nonReduxDispatch(store, message, instanceId, action, state, instances),
|
||||
instanceId,
|
||||
id
|
||||
});
|
||||
}
|
||||
|
||||
function monitoring(request) {
|
||||
if (request.type === 'DISCONNECTED') {
|
||||
store.dispatch({
|
||||
type: REMOVE_INSTANCE,
|
||||
id: request.id
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (request.type === 'START') {
|
||||
store.dispatch({ type: actions.EMIT, message: 'START', id: request.id });
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.type === 'ERROR') {
|
||||
store.dispatch(showNotification(request.payload));
|
||||
return;
|
||||
}
|
||||
|
||||
store.dispatch({
|
||||
type: UPDATE_STATE,
|
||||
request: request.data ? { ...request.data, id: request.id } : request
|
||||
});
|
||||
|
||||
const instances = store.getState().instances;
|
||||
const instanceId = request.instanceId || request.id;
|
||||
if (
|
||||
instances.sync && instanceId === instances.selected &&
|
||||
(request.type === 'ACTION' || request.type === 'STATE')
|
||||
) {
|
||||
socket.emit('respond', {
|
||||
type: 'SYNC',
|
||||
state: stringify(instances.states[instanceId]),
|
||||
id: request.id,
|
||||
instanceId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function subscribe(channelName, subscription) {
|
||||
const channel = socket.subscribe(channelName);
|
||||
if (subscription === UPDATE_STATE) channel.watch(monitoring);
|
||||
else {
|
||||
const watcher = request => {
|
||||
store.dispatch({ type: subscription, request });
|
||||
};
|
||||
channel.watch(watcher);
|
||||
socket.on(channelName, watcher);
|
||||
}
|
||||
}
|
||||
|
||||
function handleConnection() {
|
||||
socket.on('connect', status => {
|
||||
store.dispatch({
|
||||
type: actions.CONNECT_SUCCESS,
|
||||
payload: {
|
||||
id: status.id,
|
||||
authState: socket.authState,
|
||||
socketState: socket.state
|
||||
},
|
||||
error: status.authError
|
||||
});
|
||||
if (socket.authState !== actions.AUTHENTICATED) {
|
||||
store.dispatch({ type: actions.AUTH_REQUEST });
|
||||
}
|
||||
});
|
||||
socket.on('disconnect', code => {
|
||||
store.dispatch({ type: actions.DISCONNECTED, code });
|
||||
});
|
||||
|
||||
socket.on('subscribe', channel => {
|
||||
store.dispatch({ type: actions.SUBSCRIBE_SUCCESS, channel });
|
||||
});
|
||||
socket.on('unsubscribe', channel => {
|
||||
socket.unsubscribe(channel);
|
||||
socket.unwatch(channel);
|
||||
socket.off(channel);
|
||||
store.dispatch({ type: actions.UNSUBSCRIBE, channel });
|
||||
});
|
||||
socket.on('subscribeFail', error => {
|
||||
store.dispatch({ type: actions.SUBSCRIBE_ERROR, error, status: 'subscribeFail' });
|
||||
});
|
||||
socket.on('dropOut', error => {
|
||||
store.dispatch({ type: actions.SUBSCRIBE_ERROR, error, status: 'dropOut' });
|
||||
});
|
||||
|
||||
socket.on('error', error => {
|
||||
store.dispatch({ type: actions.CONNECT_ERROR, error });
|
||||
});
|
||||
}
|
||||
|
||||
function connect() {
|
||||
if (process.env.NODE_ENV === 'test') return;
|
||||
const connection = store.getState().connection;
|
||||
try {
|
||||
socket = socketCluster.connect(
|
||||
connection.type === 'remotedev' ? socketOptions : connection.options
|
||||
);
|
||||
handleConnection(store);
|
||||
} catch (error) {
|
||||
store.dispatch({ type: actions.CONNECT_ERROR, error });
|
||||
store.dispatch(showNotification(error.message || error));
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
socket.disconnect();
|
||||
socket.off();
|
||||
}
|
||||
|
||||
function login() {
|
||||
socket.emit('login', {}, (error, baseChannel) => {
|
||||
if (error) {
|
||||
store.dispatch({ type: actions.AUTH_ERROR, error });
|
||||
return;
|
||||
}
|
||||
store.dispatch({ type: actions.AUTH_SUCCESS, baseChannel });
|
||||
store.dispatch({
|
||||
type: actions.SUBSCRIBE_REQUEST,
|
||||
channel: baseChannel,
|
||||
subscription: UPDATE_STATE
|
||||
});
|
||||
store.dispatch({
|
||||
type: actions.SUBSCRIBE_REQUEST,
|
||||
channel: 'report',
|
||||
subscription: UPDATE_REPORTS
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getReport(reportId) {
|
||||
socket.emit('getReport', reportId, (error, data) => {
|
||||
if (error) {
|
||||
store.dispatch({ type: GET_REPORT_ERROR, error });
|
||||
return;
|
||||
}
|
||||
store.dispatch({ type: GET_REPORT_SUCCESS, data });
|
||||
store.dispatch(importState(data.payload));
|
||||
});
|
||||
}
|
||||
|
||||
export default function api(inStore) {
|
||||
store = inStore;
|
||||
return next => action => {
|
||||
const result = next(action);
|
||||
switch (action.type) { // eslint-disable-line default-case
|
||||
case actions.CONNECT_REQUEST: connect(); break;
|
||||
case actions.RECONNECT:
|
||||
disconnect();
|
||||
if (action.options.type !== 'disabled') connect();
|
||||
break;
|
||||
case actions.AUTH_REQUEST: login(); break;
|
||||
case actions.SUBSCRIBE_REQUEST: subscribe(action.channel, action.subscription); break;
|
||||
case actions.SUBSCRIBE_SUCCESS: startMonitoring(action.channel); break;
|
||||
case actions.EMIT: if (socket) emit(action); break;
|
||||
case LIFTED_ACTION: dispatchRemoteAction(action); break;
|
||||
case GET_REPORT_REQUEST: getReport(action.report); break;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import stringifyJSON from '../utils/stringifyJSON';
|
||||
import { UPDATE_STATE, LIFTED_ACTION, EXPORT } from '../constants/actionTypes';
|
||||
import { getActiveInstance } from '../reducers/instances';
|
||||
|
||||
let toExport;
|
||||
|
||||
function download(state) {
|
||||
const blob = new Blob([state], { type: 'octet/stream' });
|
||||
const href = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style = 'display: none';
|
||||
a.download = 'state.json';
|
||||
a.href = href;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(href);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
const exportState = store => next => action => {
|
||||
const result = next(action);
|
||||
|
||||
if (toExport && action.type === UPDATE_STATE && action.request.type === 'EXPORT') {
|
||||
const request = action.request;
|
||||
const id = request.instanceId || request.id;
|
||||
if (id === toExport) {
|
||||
toExport = undefined;
|
||||
download(JSON.stringify({
|
||||
payload: request.payload, preloadedState: request.committedState
|
||||
}, null, '\t'));
|
||||
}
|
||||
} else if (action.type === EXPORT) {
|
||||
const instances = store.getState().instances;
|
||||
const instanceId = getActiveInstance(instances);
|
||||
const options = instances.options[instanceId];
|
||||
if (options.features.export === true) {
|
||||
download(stringifyJSON(instances.states[instanceId], options.serialize));
|
||||
} else {
|
||||
toExport = instanceId;
|
||||
next({ type: LIFTED_ACTION, message: 'EXPORT', toExport: true });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export default exportState;
|
15
packages/redux-devtools-app/src/app/reducers/connection.js
Normal file
15
packages/redux-devtools-app/src/app/reducers/connection.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { RECONNECT } from '../constants/socketActionTypes';
|
||||
|
||||
export default function connection(
|
||||
state = {
|
||||
options: { hostname: 'localhost', port: 8000, secure: false },
|
||||
type: 'remotedev'
|
||||
},
|
||||
action
|
||||
) {
|
||||
if (action.type === RECONNECT) {
|
||||
const { type, ...options } = action.options;
|
||||
return { ...state, type, options };
|
||||
}
|
||||
return state;
|
||||
}
|
22
packages/redux-devtools-app/src/app/reducers/index.js
Normal file
22
packages/redux-devtools-app/src/app/reducers/index.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import section from './section';
|
||||
import connection from './connection';
|
||||
import socket from './socket';
|
||||
import monitor from './monitor';
|
||||
import notification from './notification';
|
||||
import instances from './instances';
|
||||
import reports from './reports';
|
||||
import theme from './theme';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
section,
|
||||
theme,
|
||||
connection,
|
||||
socket,
|
||||
monitor,
|
||||
instances,
|
||||
reports,
|
||||
notification
|
||||
});
|
||||
|
||||
export default rootReducer;
|
299
packages/redux-devtools-app/src/app/reducers/instances.js
Normal file
299
packages/redux-devtools-app/src/app/reducers/instances.js
Normal file
|
@ -0,0 +1,299 @@
|
|||
import {
|
||||
UPDATE_STATE, SET_STATE, LIFTED_ACTION,
|
||||
SELECT_INSTANCE, REMOVE_INSTANCE, TOGGLE_PERSIST, TOGGLE_SYNC
|
||||
} from '../constants/actionTypes';
|
||||
import { DISCONNECTED } from '../constants/socketActionTypes';
|
||||
import parseJSON from '../utils/parseJSON';
|
||||
import { recompute } from '../utils/updateState';
|
||||
|
||||
export const initialState = {
|
||||
selected: null,
|
||||
current: 'default',
|
||||
sync: false,
|
||||
connections: {},
|
||||
options: { default: { features: {} } },
|
||||
states: {
|
||||
default: {
|
||||
actionsById: {},
|
||||
computedStates: [],
|
||||
currentStateIndex: -1,
|
||||
nextActionId: 0,
|
||||
skippedActionIds: [],
|
||||
stagedActionIds: []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function updateState(state, request, id, serialize) {
|
||||
let payload = request.payload;
|
||||
const actionsById = request.actionsById;
|
||||
if (actionsById) {
|
||||
payload = {
|
||||
...payload,
|
||||
actionsById: parseJSON(actionsById, serialize),
|
||||
computedStates: parseJSON(request.computedStates, serialize)
|
||||
};
|
||||
if (request.type === 'STATE' && request.committedState) {
|
||||
payload.committedState = payload.computedStates[0].state;
|
||||
}
|
||||
} else {
|
||||
payload = parseJSON(payload, serialize);
|
||||
}
|
||||
|
||||
let newState;
|
||||
const liftedState = state[id] || state.default;
|
||||
const action = request.action && parseJSON(request.action, serialize) || {};
|
||||
|
||||
switch (request.type) {
|
||||
case 'INIT':
|
||||
newState = recompute(
|
||||
state.default,
|
||||
payload,
|
||||
{ action: { type: '@@INIT' }, timestamp: action.timestamp || Date.now() }
|
||||
);
|
||||
break;
|
||||
case 'ACTION': {
|
||||
let isExcess = request.isExcess;
|
||||
const nextActionId = request.nextActionId || (liftedState.nextActionId + 1);
|
||||
const maxAge = request.maxAge;
|
||||
if (Array.isArray(action)) {
|
||||
// Batched actions
|
||||
newState = liftedState;
|
||||
for (let i = 0; i < action.length; i++) {
|
||||
newState = recompute(
|
||||
newState,
|
||||
request.batched ? payload : payload[i],
|
||||
action[i],
|
||||
newState.nextActionId + 1,
|
||||
maxAge,
|
||||
isExcess
|
||||
);
|
||||
}
|
||||
} else {
|
||||
newState = recompute(
|
||||
liftedState,
|
||||
payload,
|
||||
action,
|
||||
nextActionId,
|
||||
maxAge,
|
||||
isExcess
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'STATE':
|
||||
newState = payload;
|
||||
if (newState.computedStates.length <= newState.currentStateIndex) {
|
||||
newState.currentStateIndex = newState.computedStates.length - 1;
|
||||
}
|
||||
break;
|
||||
case 'PARTIAL_STATE': {
|
||||
const maxAge = request.maxAge;
|
||||
const nextActionId = payload.nextActionId;
|
||||
const stagedActionIds = payload.stagedActionIds;
|
||||
let computedStates = payload.computedStates;
|
||||
let oldActionsById;
|
||||
let oldComputedStates;
|
||||
let committedState;
|
||||
if (nextActionId > maxAge) {
|
||||
const oldStagedActionIds = liftedState.stagedActionIds;
|
||||
const excess = oldStagedActionIds.indexOf(stagedActionIds[1]);
|
||||
let key;
|
||||
if (excess > 0) {
|
||||
oldComputedStates = liftedState.computedStates.slice(excess - 1);
|
||||
oldActionsById = { ...liftedState.actionsById };
|
||||
for (let i = 1; i < excess; i++) {
|
||||
key = oldStagedActionIds[i];
|
||||
if (key) delete oldActionsById[key];
|
||||
}
|
||||
committedState = computedStates[0].state;
|
||||
} else {
|
||||
oldActionsById = liftedState.actionsById;
|
||||
oldComputedStates = liftedState.computedStates;
|
||||
committedState = liftedState.committedState;
|
||||
}
|
||||
} else {
|
||||
oldActionsById = liftedState.actionsById;
|
||||
oldComputedStates = liftedState.computedStates;
|
||||
committedState = liftedState.committedState;
|
||||
}
|
||||
computedStates = [...oldComputedStates, ...computedStates];
|
||||
const statesCount = computedStates.length;
|
||||
let currentStateIndex = payload.currentStateIndex;
|
||||
if (statesCount <= currentStateIndex) currentStateIndex = statesCount - 1;
|
||||
|
||||
newState = {
|
||||
...liftedState,
|
||||
actionsById: { ...oldActionsById, ...payload.actionsById },
|
||||
computedStates,
|
||||
currentStateIndex,
|
||||
nextActionId,
|
||||
stagedActionIds,
|
||||
committedState
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'LIFTED':
|
||||
newState = liftedState;
|
||||
break;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
if (request.liftedState) newState = { ...newState, ...request.liftedState };
|
||||
return { ...state, [id]: newState };
|
||||
}
|
||||
|
||||
export function dispatchAction(state, { action }) {
|
||||
if (action.type === 'JUMP_TO_STATE' || action.type === 'JUMP_TO_ACTION') {
|
||||
const id = state.selected || state.current;
|
||||
const liftedState = state.states[id];
|
||||
let currentStateIndex = action.index;
|
||||
if (typeof currentStateIndex === 'undefined' && action.actionId) {
|
||||
currentStateIndex = liftedState.stagedActionIds.indexOf(action.actionId);
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
states: {
|
||||
...state.states,
|
||||
[id]: { ...liftedState, currentStateIndex }
|
||||
}
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function removeState(state, connectionId) {
|
||||
const instanceIds = state.connections[connectionId];
|
||||
if (!instanceIds) return state;
|
||||
|
||||
const connections = { ...state.connections };
|
||||
const options = { ...state.options };
|
||||
const states = { ...state.states };
|
||||
let selected = state.selected;
|
||||
let current = state.current;
|
||||
let sync = state.sync;
|
||||
|
||||
delete connections[connectionId];
|
||||
instanceIds.forEach(id => {
|
||||
if (id === selected) {
|
||||
selected = null;
|
||||
sync = false;
|
||||
}
|
||||
if (id === current) {
|
||||
const inst = Object.keys(connections)[0];
|
||||
if (inst) current = connections[inst][0];
|
||||
else current = 'default';
|
||||
}
|
||||
delete options[id];
|
||||
delete states[id];
|
||||
});
|
||||
return {
|
||||
selected,
|
||||
current,
|
||||
sync,
|
||||
connections,
|
||||
options,
|
||||
states
|
||||
};
|
||||
}
|
||||
|
||||
function init({ type, action, name, libConfig = {} }, connectionId, current) {
|
||||
let lib;
|
||||
let actionCreators;
|
||||
let creators = libConfig.actionCreators || action;
|
||||
if (typeof creators === 'string') creators = JSON.parse(creators);
|
||||
if (Array.isArray(creators)) actionCreators = creators;
|
||||
if (type === 'STATE') lib = 'redux';
|
||||
return {
|
||||
name: libConfig.name || name || current,
|
||||
connectionId,
|
||||
explicitLib: libConfig.type,
|
||||
lib,
|
||||
actionCreators,
|
||||
features: libConfig.features ? libConfig.features :
|
||||
{
|
||||
lock: lib === 'redux',
|
||||
export: libConfig.type === 'redux' ? 'custom' : true,
|
||||
import: 'custom',
|
||||
persist: true,
|
||||
pause: true,
|
||||
reorder: true,
|
||||
jump: true,
|
||||
skip: true,
|
||||
dispatch: true,
|
||||
sync: true,
|
||||
test: true
|
||||
},
|
||||
serialize: libConfig.serialize
|
||||
};
|
||||
}
|
||||
|
||||
export default function instances(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case UPDATE_STATE: {
|
||||
const { request } = action;
|
||||
if (!request) return state;
|
||||
const connectionId = action.id || request.id;
|
||||
const current = request.instanceId || connectionId;
|
||||
let connections = state.connections;
|
||||
let options = state.options;
|
||||
|
||||
if (typeof state.options[current] === 'undefined') {
|
||||
connections = {
|
||||
...state.connections,
|
||||
[connectionId]: [...(connections[connectionId] || []), current]
|
||||
};
|
||||
options = { ...options, [current]: init(request, connectionId, current) };
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
current,
|
||||
connections,
|
||||
options,
|
||||
states: updateState(state.states, request, current, options[current].serialize)
|
||||
};
|
||||
}
|
||||
case SET_STATE:
|
||||
return {
|
||||
...state,
|
||||
states: {
|
||||
...state.states,
|
||||
[getActiveInstance(state)]: action.newState
|
||||
}
|
||||
};
|
||||
case TOGGLE_PERSIST:
|
||||
return { ...state, persisted: !state.persisted };
|
||||
case TOGGLE_SYNC:
|
||||
return { ...state, sync: !state.sync };
|
||||
case SELECT_INSTANCE:
|
||||
return { ...state, selected: action.selected, sync: false };
|
||||
case REMOVE_INSTANCE:
|
||||
return removeState(state, action.id);
|
||||
case LIFTED_ACTION: {
|
||||
if (action.message === 'DISPATCH') return dispatchAction(state, action);
|
||||
if (action.message === 'IMPORT') {
|
||||
const id = state.selected || state.current;
|
||||
if (state.options[id].features.import === true) {
|
||||
return {
|
||||
...state,
|
||||
states: {
|
||||
...state.states,
|
||||
[id]: parseJSON(action.state)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
case DISCONNECTED:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable no-shadow */
|
||||
export const getActiveInstance = instances => instances.selected || instances.current;
|
||||
/* eslint-enable */
|
69
packages/redux-devtools-app/src/app/reducers/monitor.js
Normal file
69
packages/redux-devtools-app/src/app/reducers/monitor.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
import {
|
||||
MONITOR_ACTION, SELECT_MONITOR, SELECT_MONITOR_TAB, UPDATE_MONITOR_STATE,
|
||||
TOGGLE_SLIDER, TOGGLE_DISPATCHER
|
||||
} from '../constants/actionTypes';
|
||||
|
||||
const initialState = {
|
||||
selected: 'InspectorMonitor',
|
||||
monitorState: undefined,
|
||||
sliderIsOpen: true,
|
||||
dispatcherIsOpen: false
|
||||
};
|
||||
|
||||
export function dispatchMonitorAction(state, action) {
|
||||
return {
|
||||
...state,
|
||||
monitorState: action.action.newMonitorState ||
|
||||
action.monitorReducer(action.monitorProps, state.monitorState, action.action)
|
||||
};
|
||||
}
|
||||
|
||||
export default function monitor(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case MONITOR_ACTION:
|
||||
return dispatchMonitorAction(state, action);
|
||||
case SELECT_MONITOR: {
|
||||
let monitorState = state.monitorState;
|
||||
if (action.monitorState) {
|
||||
monitorState = {
|
||||
...action.monitorState,
|
||||
__overwritten__: action.monitor
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
monitorState,
|
||||
selected: action.monitor
|
||||
};
|
||||
}
|
||||
case UPDATE_MONITOR_STATE: {
|
||||
let inspectedStatePath = state.monitorState.inspectedStatePath;
|
||||
if (action.nextState.inspectedStatePath) {
|
||||
inspectedStatePath = [
|
||||
...inspectedStatePath.slice(0, -1),
|
||||
...action.nextState.inspectedStatePath
|
||||
];
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
monitorState: {
|
||||
...state.monitorState,
|
||||
...action.nextState,
|
||||
inspectedStatePath
|
||||
}
|
||||
};
|
||||
}
|
||||
case TOGGLE_SLIDER:
|
||||
return {
|
||||
...state,
|
||||
sliderIsOpen: !state.sliderIsOpen
|
||||
};
|
||||
case TOGGLE_DISPATCHER:
|
||||
return {
|
||||
...state,
|
||||
dispatcherIsOpen: !state.dispatcherIsOpen
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
16
packages/redux-devtools-app/src/app/reducers/notification.js
Normal file
16
packages/redux-devtools-app/src/app/reducers/notification.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { SHOW_NOTIFICATION, CLEAR_NOTIFICATION, LIFTED_ACTION, ERROR } from '../constants/actionTypes';
|
||||
|
||||
export default function notification(state = null, action) {
|
||||
switch (action.type) {
|
||||
case SHOW_NOTIFICATION:
|
||||
return action.notification;
|
||||
case ERROR:
|
||||
return { type: 'error', message: action.payload };
|
||||
case LIFTED_ACTION:
|
||||
return null;
|
||||
case CLEAR_NOTIFICATION:
|
||||
return null;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
37
packages/redux-devtools-app/src/app/reducers/reports.js
Normal file
37
packages/redux-devtools-app/src/app/reducers/reports.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { UPDATE_REPORTS, GET_REPORT_SUCCESS } from '../constants/actionTypes';
|
||||
|
||||
const initialState = {
|
||||
data: []
|
||||
};
|
||||
|
||||
export default function reports(state = initialState, action) {
|
||||
/* if (action.type === GET_REPORT_SUCCESS) {
|
||||
const id = action.data.id;
|
||||
return {
|
||||
...state,
|
||||
data: state.data.map(d => (d.id === id ? action.data : d))
|
||||
};
|
||||
} else */ if (action.type !== UPDATE_REPORTS) return state;
|
||||
|
||||
const request = action.request;
|
||||
const data = request.data;
|
||||
switch (request.type) {
|
||||
case 'list':
|
||||
return {
|
||||
...state,
|
||||
data
|
||||
};
|
||||
case 'add':
|
||||
return {
|
||||
...state,
|
||||
data: [...state.data, data]
|
||||
};
|
||||
case 'remove':
|
||||
return {
|
||||
...state,
|
||||
data: state.data.filter(d => d.id !== request.id)
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
8
packages/redux-devtools-app/src/app/reducers/section.js
Normal file
8
packages/redux-devtools-app/src/app/reducers/section.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { CHANGE_SECTION } from '../constants/actionTypes';
|
||||
|
||||
export default function section(state = 'Actions', action) {
|
||||
if (action.type === CHANGE_SECTION) {
|
||||
return action.section;
|
||||
}
|
||||
return state;
|
||||
}
|
74
packages/redux-devtools-app/src/app/reducers/socket.js
Normal file
74
packages/redux-devtools-app/src/app/reducers/socket.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
import * as actions from '../constants/socketActionTypes';
|
||||
|
||||
const initialState = {
|
||||
id: null,
|
||||
channels: [],
|
||||
socketState: actions.CLOSED,
|
||||
authState: actions.PENDING,
|
||||
authToken: null,
|
||||
error: undefined
|
||||
};
|
||||
|
||||
export default function socket(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case actions.CONNECT_REQUEST: {
|
||||
return {
|
||||
...state,
|
||||
socketState: actions.CONNECTING
|
||||
};
|
||||
}
|
||||
case actions.CONNECT_ERROR:
|
||||
return {
|
||||
...state,
|
||||
error: action.error
|
||||
};
|
||||
case actions.CONNECT_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
id: action.payload.id,
|
||||
socketState: action.payload.socketState,
|
||||
authState: action.payload.authState,
|
||||
error: action.error
|
||||
};
|
||||
case actions.AUTH_REQUEST:
|
||||
return {
|
||||
...state,
|
||||
authState: actions.PENDING
|
||||
};
|
||||
case actions.AUTH_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
authState: actions.AUTHENTICATED,
|
||||
authToken: action.authToken,
|
||||
baseChannel: action.baseChannel
|
||||
};
|
||||
case actions.AUTH_ERROR:
|
||||
return {
|
||||
...state,
|
||||
authState: actions.UNAUTHENTICATED,
|
||||
error: action.error
|
||||
};
|
||||
case actions.DEAUTHENTICATE:
|
||||
return {
|
||||
...state,
|
||||
authState: actions.UNAUTHENTICATED,
|
||||
authToken: null
|
||||
};
|
||||
case actions.SUBSCRIBE_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
channels: [...state.channels, action.channelName]
|
||||
};
|
||||
case actions.UNSUBSCRIBE:
|
||||
return {
|
||||
...state,
|
||||
channels: state.channels.filter(channel =>
|
||||
channel !== action.channelName
|
||||
)
|
||||
};
|
||||
case actions.DISCONNECTED:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
15
packages/redux-devtools-app/src/app/reducers/theme.js
Normal file
15
packages/redux-devtools-app/src/app/reducers/theme.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { CHANGE_THEME } from '../constants/actionTypes';
|
||||
|
||||
export default function theme(
|
||||
state = { theme: 'default', scheme: 'default', light: true },
|
||||
action
|
||||
) {
|
||||
if (action.type === CHANGE_THEME) {
|
||||
return {
|
||||
theme: action.theme,
|
||||
scheme: action.scheme,
|
||||
light: !action.dark
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
39
packages/redux-devtools-app/src/app/store/configureStore.js
Normal file
39
packages/redux-devtools-app/src/app/store/configureStore.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { createStore, compose, applyMiddleware } from 'redux';
|
||||
import localForage from 'localforage';
|
||||
import { getStoredState, createPersistor } from 'redux-persist';
|
||||
import api from '../middlewares/api';
|
||||
import exportState from '../middlewares/exportState';
|
||||
import rootReducer from '../reducers';
|
||||
|
||||
export default function configureStore(callback, key) {
|
||||
const persistConfig = {
|
||||
keyPrefix: `remotedev${key || ''}:`,
|
||||
blacklist: ['instances', 'socket'],
|
||||
storage: localForage,
|
||||
serialize: data => data,
|
||||
deserialize: data => data
|
||||
};
|
||||
|
||||
getStoredState(persistConfig, (err, restoredState) => {
|
||||
let composeEnhancers = compose;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) {
|
||||
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
|
||||
}
|
||||
if (module.hot) {
|
||||
// Enable Webpack hot module replacement for reducers
|
||||
module.hot.accept('../reducers', () => {
|
||||
const nextReducer = require('../reducers'); // eslint-disable-line global-require
|
||||
store.replaceReducer(nextReducer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const store = createStore(rootReducer, restoredState, composeEnhancers(
|
||||
applyMiddleware(exportState, api)
|
||||
));
|
||||
const persistor = createPersistor(store, persistConfig);
|
||||
callback(store, restoredState);
|
||||
if (err) persistor.purge();
|
||||
});
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Based on https://github.com/gaearon/redux-devtools/pull/241
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
export default function commitExcessActions(liftedState, n = 1) {
|
||||
// Auto-commits n-number of excess actions.
|
||||
let excess = n;
|
||||
let idsToDelete = liftedState.stagedActionIds.slice(1, excess + 1);
|
||||
|
||||
for (let i = 0; i < idsToDelete.length; i++) {
|
||||
if (liftedState.computedStates[i + 1].error) {
|
||||
// Stop if error is found. Commit actions up to error.
|
||||
excess = i;
|
||||
idsToDelete = liftedState.stagedActionIds.slice(1, excess + 1);
|
||||
break;
|
||||
} else {
|
||||
delete liftedState.actionsById[idsToDelete[i]];
|
||||
}
|
||||
}
|
||||
|
||||
liftedState.skippedActionIds = liftedState.skippedActionIds.filter(
|
||||
id => idsToDelete.indexOf(id) === -1
|
||||
);
|
||||
liftedState.stagedActionIds = [0, ...liftedState.stagedActionIds.slice(excess + 1)];
|
||||
liftedState.committedState = liftedState.computedStates[excess].state;
|
||||
liftedState.computedStates = liftedState.computedStates.slice(excess);
|
||||
liftedState.currentStateIndex = liftedState.currentStateIndex > excess
|
||||
? liftedState.currentStateIndex - excess
|
||||
: 0;
|
||||
}
|
22
packages/redux-devtools-app/src/app/utils/getMonitor.js
Normal file
22
packages/redux-devtools-app/src/app/utils/getMonitor.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import LogMonitor from 'redux-devtools-log-monitor';
|
||||
import ChartMonitorWrapper from '../containers/monitors/ChartMonitorWrapper';
|
||||
import InspectorWrapper from '../containers/monitors/InspectorWrapper';
|
||||
|
||||
export const monitors = [
|
||||
{ value: 'InspectorMonitor', name: 'Inspector' },
|
||||
{ value: 'LogMonitor', name: 'Log monitor' },
|
||||
{ value: 'ChartMonitor', name: 'Chart' }
|
||||
];
|
||||
|
||||
export default function getMonitor({ monitor }) { // eslint-disable-line react/prop-types
|
||||
switch (monitor) {
|
||||
case 'LogMonitor':
|
||||
return <LogMonitor preserveScrollTop={false} hideMainButtons markStateDiff />;
|
||||
case 'ChartMonitor':
|
||||
return <ChartMonitorWrapper />;
|
||||
default:
|
||||
return <InspectorWrapper />;
|
||||
}
|
||||
}
|
50
packages/redux-devtools-app/src/app/utils/monitorActions.js
Normal file
50
packages/redux-devtools-app/src/app/utils/monitorActions.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
import difference from 'lodash/difference';
|
||||
import omit from 'lodash/omit';
|
||||
import stringifyJSON from './stringifyJSON';
|
||||
import { SET_STATE } from '../constants/actionTypes';
|
||||
|
||||
export function sweep(state) {
|
||||
return {
|
||||
...state,
|
||||
actionsById: omit(state.actionsById, state.skippedActionIds),
|
||||
stagedActionIds: difference(state.stagedActionIds, state.skippedActionIds),
|
||||
skippedActionIds: [],
|
||||
currentStateIndex: Math.min(state.currentStateIndex, state.stagedActionIds.length - 1)
|
||||
};
|
||||
}
|
||||
|
||||
export function nonReduxDispatch(store, message, instanceId, action, initialState, preInstances) {
|
||||
const instances = preInstances || store.getState().instances;
|
||||
const state = instances.states[instanceId];
|
||||
const options = instances.options[instanceId];
|
||||
|
||||
if (message !== 'DISPATCH') {
|
||||
if (message === 'IMPORT') {
|
||||
if (options.features.import === true) {
|
||||
return stringifyJSON(state.computedStates[state.currentStateIndex].state, true);
|
||||
}
|
||||
return initialState;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (options.lib === 'redux') return undefined;
|
||||
|
||||
switch (action.type) {
|
||||
case 'TOGGLE_ACTION':
|
||||
return stringifyJSON(state, true);
|
||||
case 'JUMP_TO_STATE':
|
||||
return stringifyJSON(state.computedStates[action.index].state, true);
|
||||
case 'JUMP_TO_ACTION':
|
||||
return stringifyJSON(
|
||||
state.computedStates[state.stagedActionIds.indexOf(action.actionId)].state, true
|
||||
);
|
||||
case 'ROLLBACK':
|
||||
return stringifyJSON(state.computedStates[0].state, true);
|
||||
case 'SWEEP':
|
||||
store.dispatch({ type: SET_STATE, newState: sweep(state) });
|
||||
return undefined;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
34
packages/redux-devtools-app/src/app/utils/parseJSON.js
Normal file
34
packages/redux-devtools-app/src/app/utils/parseJSON.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import jsan from 'jsan';
|
||||
import { DATA_TYPE_KEY, DATA_REF_KEY } from '../constants/dataTypes';
|
||||
|
||||
export function reviver(key, value) {
|
||||
if (
|
||||
typeof value === 'object' && value !== null &&
|
||||
'__serializedType__' in value && typeof value.data === 'object'
|
||||
) {
|
||||
const data = value.data;
|
||||
data[DATA_TYPE_KEY] = value.__serializedType__;
|
||||
if ('__serializedRef__' in value) data[DATA_REF_KEY] = value.__serializedRef__;
|
||||
/*
|
||||
if (Array.isArray(data)) {
|
||||
data.__serializedType__ = value.__serializedType__;
|
||||
} else {
|
||||
Object.defineProperty(data, '__serializedType__', {
|
||||
value: value.__serializedType__
|
||||
});
|
||||
}
|
||||
*/
|
||||
return data;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export default function parseJSON(data, serialize) {
|
||||
if (typeof data !== 'string') return data;
|
||||
try {
|
||||
return serialize ? jsan.parse(data, reviver) : jsan.parse(data);
|
||||
} catch (e) {
|
||||
if (process.env.NODE_ENV !== 'production') console.error(data + 'is not a valid JSON', e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
17
packages/redux-devtools-app/src/app/utils/stringifyJSON.js
Normal file
17
packages/redux-devtools-app/src/app/utils/stringifyJSON.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import jsan from 'jsan';
|
||||
import { DATA_TYPE_KEY, DATA_REF_KEY } from '../constants/dataTypes';
|
||||
|
||||
function replacer(key, value) {
|
||||
if (typeof value === 'object' && value !== null && DATA_TYPE_KEY in value) {
|
||||
const __serializedType__ = value[DATA_TYPE_KEY];
|
||||
delete value[DATA_TYPE_KEY]; // eslint-disable-line no-param-reassign
|
||||
const r = { data: value, __serializedType__ };
|
||||
if (DATA_REF_KEY in value) r.__serializedRef__ = value[DATA_REF_KEY];
|
||||
return r;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export default function stringifyJSON(data, serialize) {
|
||||
return serialize ? jsan.stringify(data, replacer, null, true) : jsan.stringify(data);
|
||||
}
|
32
packages/redux-devtools-app/src/app/utils/updateState.js
Normal file
32
packages/redux-devtools-app/src/app/utils/updateState.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import commitExcessActions from './commitExcessActions';
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export function recompute(previousLiftedState, storeState, action, nextActionId = 1, maxAge, isExcess) {
|
||||
const actionId = nextActionId - 1;
|
||||
const liftedState = { ...previousLiftedState };
|
||||
|
||||
if (liftedState.currentStateIndex === liftedState.stagedActionIds.length - 1) {
|
||||
liftedState.currentStateIndex++;
|
||||
}
|
||||
liftedState.stagedActionIds = [...liftedState.stagedActionIds, actionId];
|
||||
liftedState.actionsById = { ...liftedState.actionsById };
|
||||
if (action.type === 'PERFORM_ACTION') {
|
||||
liftedState.actionsById[actionId] = action;
|
||||
} else {
|
||||
liftedState.actionsById[actionId] = {
|
||||
action: action.action || action,
|
||||
timestamp: action.timestamp || Date.now(),
|
||||
type: 'PERFORM_ACTION'
|
||||
};
|
||||
}
|
||||
liftedState.nextActionId = nextActionId;
|
||||
liftedState.computedStates = [...liftedState.computedStates, { state: storeState }];
|
||||
|
||||
if (isExcess) commitExcessActions(liftedState);
|
||||
else if (maxAge) {
|
||||
const excess = liftedState.stagedActionIds.length - maxAge;
|
||||
if (excess > 0) commitExcessActions(liftedState, excess);
|
||||
}
|
||||
|
||||
return liftedState;
|
||||
}
|
25
packages/redux-devtools-app/src/index.js
Normal file
25
packages/redux-devtools-app/src/index.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import App from './app';
|
||||
|
||||
render(
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
// https://github.com/webpack/webpack/issues/418#issuecomment-53398056
|
||||
module.hot.accept(err => {
|
||||
if (err) console.error(err.message);
|
||||
});
|
||||
|
||||
/*
|
||||
module.hot.accept('./app', () => {
|
||||
const NextApp = require('./app').default;
|
||||
render(
|
||||
<NextApp />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
});
|
||||
*/
|
||||
}
|
1
packages/redux-devtools-app/test/__mocks__/styleMock.js
Normal file
1
packages/redux-devtools-app/test/__mocks__/styleMock.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = {};
|
41
packages/redux-devtools-app/test/index.spec.js
Normal file
41
packages/redux-devtools-app/test/index.spec.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import { mount } from 'enzyme';
|
||||
// import { mountToJson } from 'enzyme-to-json';
|
||||
import App from '../src/app/containers/App';
|
||||
import api from '../src/app/middlewares/api';
|
||||
import exportState from '../src/app/middlewares/exportState';
|
||||
import rootReducer from '../src/app/reducers';
|
||||
let wrapper;
|
||||
|
||||
const store = createStore(rootReducer, applyMiddleware(exportState, api));
|
||||
|
||||
describe('App container', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
it('should render the App', () => {
|
||||
expect(mountToJson(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
*/
|
||||
|
||||
it('should render inspector monitor\'s wrapper', () => {
|
||||
expect(wrapper.find('DevtoolsInspector').html()).toBeDefined();
|
||||
});
|
||||
|
||||
it('should contain an empty action list', () => {
|
||||
expect(
|
||||
wrapper.find('ActionList').findWhere(n => {
|
||||
const { className } = n.props();
|
||||
return className && className.startsWith('actionListRows-');
|
||||
}).html()
|
||||
).toMatch(/<div class="actionListRows-[0-9]+"><\/div>/);
|
||||
});
|
||||
});
|
82
packages/redux-devtools-app/webpack.config.js
Normal file
82
packages/redux-devtools-app/webpack.config.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = (env = {}) => (
|
||||
{
|
||||
entry: {
|
||||
app: './src/index.js',
|
||||
common: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-redux',
|
||||
'redux',
|
||||
'redux-persist',
|
||||
'localforage',
|
||||
'styled-components',
|
||||
'jsan',
|
||||
'socketcluster-client'
|
||||
]
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'build/' + env.platform),
|
||||
publicPath: '',
|
||||
filename: 'js/[name].js',
|
||||
sourceMapFilename: 'js/[name].map'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'html-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
{ loader: 'style-loader' },
|
||||
{ loader: 'css-loader' }
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(png|gif|jpg)$/,
|
||||
loader: 'url-loader',
|
||||
options: { limit: '25000', outputPath: 'images/', publicPath: 'images/' }
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|svg|woff|woff2)$/,
|
||||
loader: 'file-loader',
|
||||
options: { outputPath: 'fonts/', publicPath: 'fonts/' }
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(env.development ? 'development' : 'production'),
|
||||
PLATFORM: JSON.stringify(env.platform)
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
names: ['common'],
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'assets/index.html'
|
||||
}),
|
||||
new CopyWebpackPlugin(
|
||||
env.platform === 'electron' ? [
|
||||
{ context: './src/electron', from: '*' }
|
||||
] : []
|
||||
)
|
||||
],
|
||||
devServer: {
|
||||
port: 3000
|
||||
},
|
||||
devtool: env.development ? 'eval' : 'source-map'
|
||||
}
|
||||
);
|
67
packages/redux-devtools-app/webpack.config.umd.js
Normal file
67
packages/redux-devtools-app/webpack.config.umd.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = (env = {}) => (
|
||||
{
|
||||
entry: {
|
||||
app: ['./src/app/index.js']
|
||||
},
|
||||
output: {
|
||||
library: 'RemoteDevApp',
|
||||
libraryTarget: 'umd',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: env.development ? 'remotedev-app.js' : 'remotedev-app.min.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'html-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
{ loader: 'style-loader' },
|
||||
{ loader: 'css-loader' }
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(png|gif|jpg)$/,
|
||||
loader: 'url-loader',
|
||||
options: { limit: '25000' }
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|svg|woff|woff2)$/,
|
||||
loader: 'url-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify('production'),
|
||||
PLATFORM: JSON.stringify('web')
|
||||
}
|
||||
})
|
||||
],
|
||||
externals: {
|
||||
react: {
|
||||
root: 'React',
|
||||
commonjs2: 'react',
|
||||
commonjs: 'react',
|
||||
amd: 'react'
|
||||
},
|
||||
'react-dom': {
|
||||
root: 'ReactDOM',
|
||||
commonjs2: 'react-dom',
|
||||
commonjs: 'react-dom',
|
||||
amd: 'react-dom'
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
253
yarn.lock
253
yarn.lock
|
@ -1726,7 +1726,7 @@ axobject-query@^2.0.1:
|
|||
dependencies:
|
||||
ast-types-flow "0.0.7"
|
||||
|
||||
babel-cli@^6.10.1, babel-cli@^6.24.1, babel-cli@^6.26.0, babel-cli@^6.3.15, babel-cli@^6.3.17, babel-cli@^6.4.5:
|
||||
babel-cli@^6.10.1, babel-cli@^6.22.2, babel-cli@^6.24.1, babel-cli@^6.26.0, babel-cli@^6.3.15, babel-cli@^6.3.17, babel-cli@^6.4.5:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.26.0.tgz#502ab54874d7db88ad00b887a06383ce03d002f1"
|
||||
integrity sha1-UCq1SHTX24itALiHoGODzgPQAvE=
|
||||
|
@ -1809,7 +1809,7 @@ babel-core@^5.1.8, babel-core@^5.8.25, babel-core@^5.8.33:
|
|||
trim-right "^1.0.0"
|
||||
try-resolve "^1.0.0"
|
||||
|
||||
babel-core@^6.0.0, babel-core@^6.1.20, babel-core@^6.1.4, babel-core@^6.10.4, babel-core@^6.24.1, babel-core@^6.26.0, babel-core@^6.26.3, babel-core@^6.3.17, babel-core@^6.4.5:
|
||||
babel-core@^6.0.0, babel-core@^6.1.20, babel-core@^6.1.4, babel-core@^6.10.4, babel-core@^6.22.1, babel-core@^6.24.1, babel-core@^6.26.0, babel-core@^6.26.3, babel-core@^6.3.17, babel-core@^6.4.5:
|
||||
version "6.26.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207"
|
||||
integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==
|
||||
|
@ -1887,7 +1887,7 @@ babel-eslint@^6.0.2, babel-eslint@^6.0.5, babel-eslint@^6.1.2:
|
|||
lodash.assign "^4.0.0"
|
||||
lodash.pickby "^4.0.0"
|
||||
|
||||
babel-eslint@^7.1.0, babel-eslint@^7.2.2:
|
||||
babel-eslint@^7.1.0, babel-eslint@^7.1.1, babel-eslint@^7.2.2:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827"
|
||||
integrity sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=
|
||||
|
@ -2115,7 +2115,7 @@ babel-jest@^23.6.0:
|
|||
babel-plugin-istanbul "^4.1.6"
|
||||
babel-preset-jest "^23.2.0"
|
||||
|
||||
babel-loader@^6.2.0, babel-loader@^6.2.2, babel-loader@^6.2.4, babel-loader@^6.4.1:
|
||||
babel-loader@^6.2.0, babel-loader@^6.2.10, babel-loader@^6.2.2, babel-loader@^6.2.4, babel-loader@^6.4.1:
|
||||
version "6.4.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.4.1.tgz#0b34112d5b0748a8dcdbf51acf6f9bd42d50b8ca"
|
||||
integrity sha1-CzQRLVsHSKjc2/Uaz2+b1C1QuMo=
|
||||
|
@ -2327,7 +2327,7 @@ babel-plugin-react-docgen@^1.9.0:
|
|||
lodash "^4.17.0"
|
||||
react-docgen "^3.0.0-beta11"
|
||||
|
||||
babel-plugin-react-transform@^2.0.0:
|
||||
babel-plugin-react-transform@^2.0.0, babel-plugin-react-transform@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz#515bbfa996893981142d90b1f9b1635de2995109"
|
||||
integrity sha1-UVu/qZaJOYEULZCx+bFjXeKZUQk=
|
||||
|
@ -2913,7 +2913,7 @@ babel-preset-env@1.6.1:
|
|||
invariant "^2.2.2"
|
||||
semver "^5.3.0"
|
||||
|
||||
babel-preset-env@^1.6.1, babel-preset-env@^1.7.0:
|
||||
babel-preset-env@^1.1.8, babel-preset-env@^1.6.1, babel-preset-env@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a"
|
||||
integrity sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==
|
||||
|
@ -2982,7 +2982,7 @@ babel-preset-es2015-loose@^7.0.0:
|
|||
dependencies:
|
||||
modify-babel-preset "^1.0.0"
|
||||
|
||||
babel-preset-es2015@^6.24.1, babel-preset-es2015@^6.3.13, babel-preset-es2015@^6.9.0:
|
||||
babel-preset-es2015@^6.22.0, babel-preset-es2015@^6.24.1, babel-preset-es2015@^6.3.13, babel-preset-es2015@^6.9.0:
|
||||
version "6.24.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939"
|
||||
integrity sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=
|
||||
|
@ -3083,7 +3083,7 @@ babel-preset-react-app@^3.1.2:
|
|||
babel-preset-env "1.6.1"
|
||||
babel-preset-react "6.24.1"
|
||||
|
||||
babel-preset-react@6.24.1, babel-preset-react@^6.24.1, babel-preset-react@^6.3.13, babel-preset-react@^6.5.0:
|
||||
babel-preset-react@6.24.1, babel-preset-react@^6.22.0, babel-preset-react@^6.24.1, babel-preset-react@^6.3.13, babel-preset-react@^6.5.0:
|
||||
version "6.24.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380"
|
||||
integrity sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=
|
||||
|
@ -3107,7 +3107,7 @@ babel-preset-react@6.3.13:
|
|||
babel-plugin-transform-react-jsx "^6.3.13"
|
||||
babel-plugin-transform-react-jsx-source "^6.3.13"
|
||||
|
||||
babel-preset-stage-0@^6.16.0, babel-preset-stage-0@^6.24.1, babel-preset-stage-0@^6.3.13, babel-preset-stage-0@^6.5.0:
|
||||
babel-preset-stage-0@^6.16.0, babel-preset-stage-0@^6.22.0, babel-preset-stage-0@^6.24.1, babel-preset-stage-0@^6.3.13, babel-preset-stage-0@^6.5.0:
|
||||
version "6.24.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz#5642d15042f91384d7e5af8bc88b1db95b039e6a"
|
||||
integrity sha1-VkLRUEL5E4TX5a+LyIsduVsDnmo=
|
||||
|
@ -3146,7 +3146,7 @@ babel-preset-stage-3@^6.24.1:
|
|||
babel-plugin-transform-exponentiation-operator "^6.24.1"
|
||||
babel-plugin-transform-object-rest-spread "^6.22.0"
|
||||
|
||||
babel-register@^6.11.6, babel-register@^6.26.0:
|
||||
babel-register@^6.11.6, babel-register@^6.22.0, babel-register@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
|
||||
integrity sha1-btAhFz4vy0htestFxgCahW9kcHE=
|
||||
|
@ -3245,6 +3245,11 @@ balanced-match@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base-64@0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
|
||||
integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs=
|
||||
|
||||
base16@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
|
||||
|
@ -4051,6 +4056,11 @@ clone-regexp@^1.0.0:
|
|||
is-regexp "^1.0.0"
|
||||
is-supported-regexp-flag "^1.0.0"
|
||||
|
||||
clone@2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb"
|
||||
integrity sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=
|
||||
|
||||
clone@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||
|
@ -4269,6 +4279,11 @@ compare-func@^1.3.1:
|
|||
array-ify "^1.0.0"
|
||||
dot-prop "^3.0.0"
|
||||
|
||||
component-emitter@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.0.tgz#ccd113a86388d06482d03de3fc7df98526ba8efe"
|
||||
integrity sha1-zNETqGOI0GSC0D3j/H35hSa6jv4=
|
||||
|
||||
component-emitter@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
|
||||
|
@ -4489,6 +4504,20 @@ copy-descriptor@^0.1.0:
|
|||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
||||
|
||||
copy-webpack-plugin@^4.0.1:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz#e7f40dd8a68477d405dd1b7a854aae324b158bae"
|
||||
integrity sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==
|
||||
dependencies:
|
||||
cacache "^10.0.4"
|
||||
find-cache-dir "^1.0.0"
|
||||
globby "^7.1.1"
|
||||
is-glob "^4.0.0"
|
||||
loader-utils "^1.1.0"
|
||||
minimatch "^3.0.4"
|
||||
p-limit "^1.0.0"
|
||||
serialize-javascript "^1.4.0"
|
||||
|
||||
core-js@^1.0.0:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
|
@ -4885,6 +4914,23 @@ cyclist@~0.2.2:
|
|||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
|
||||
integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=
|
||||
|
||||
d3-state-visualizer@^1.3.1:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/d3-state-visualizer/-/d3-state-visualizer-1.3.2.tgz#8e3ac418aa7ee7e3f46025309f9d1c215ee385eb"
|
||||
integrity sha512-XgTRC6FXeoTt8l79cc2f3Zaah+K7DUQb3GL0zfbvoIi7zWWHV4l7OfuX9/JxxvwilKApMZwHMBJ7cJ2yWAc5IQ==
|
||||
dependencies:
|
||||
d3 "^3.5.6"
|
||||
d3tooltip "^1.2.2"
|
||||
deepmerge "^0.2.10"
|
||||
is-plain-object "2.0.1"
|
||||
map2tree "^1.4.0"
|
||||
ramda "^0.17.1"
|
||||
|
||||
d3@^3.5.6:
|
||||
version "3.5.17"
|
||||
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
|
||||
integrity sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=
|
||||
|
||||
d@1:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
|
||||
|
@ -5026,6 +5072,11 @@ deep-is@~0.1.2, deep-is@~0.1.3:
|
|||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
|
||||
|
||||
deepmerge@^0.2.10:
|
||||
version "0.2.10"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-0.2.10.tgz#8906bf9e525a4fbf1b203b2afcb4640249821219"
|
||||
integrity sha1-iQa/nlJaT78bIDsq/LRkAkmCEhk=
|
||||
|
||||
default-require-extensions@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
|
||||
|
@ -5762,7 +5813,7 @@ es6-template-regex@^0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/es6-template-regex/-/es6-template-regex-0.1.1.tgz#e517b9e0f742beeb8d3040834544fda0e4651467"
|
||||
integrity sha1-5Re54PdCvuuNMECDRUT9oORlFGc=
|
||||
|
||||
es6-templates@^0.2.3:
|
||||
es6-templates@^0.2.2, es6-templates@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/es6-templates/-/es6-templates-0.2.3.tgz#5cb9ac9fb1ded6eb1239342b81d792bbb4078ee4"
|
||||
integrity sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=
|
||||
|
@ -6095,7 +6146,7 @@ eslint-plugin-react@^5.2.2:
|
|||
doctrine "^1.2.2"
|
||||
jsx-ast-utils "^1.2.1"
|
||||
|
||||
eslint-plugin-react@^6.6.0:
|
||||
eslint-plugin-react@^6.6.0, eslint-plugin-react@^6.9.0:
|
||||
version "6.10.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78"
|
||||
integrity sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g=
|
||||
|
@ -6322,7 +6373,7 @@ eslint@^2.13.1, eslint@^2.7.0:
|
|||
text-table "~0.2.0"
|
||||
user-home "^2.0.0"
|
||||
|
||||
eslint@^3.2.0:
|
||||
eslint@^3.15.0, eslint@^3.2.0:
|
||||
version "3.19.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc"
|
||||
integrity sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=
|
||||
|
@ -7579,6 +7630,18 @@ globby@^6.0.0, globby@^6.1.0:
|
|||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
globby@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
|
||||
integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA=
|
||||
dependencies:
|
||||
array-union "^1.0.1"
|
||||
dir-glob "^2.0.0"
|
||||
glob "^7.1.2"
|
||||
ignore "^3.3.5"
|
||||
pify "^3.0.0"
|
||||
slash "^1.0.0"
|
||||
|
||||
globby@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50"
|
||||
|
@ -7876,6 +7939,17 @@ html-entities@^1.2.0, html-entities@^1.2.1:
|
|||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
|
||||
integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
|
||||
|
||||
html-loader@^0.4.4:
|
||||
version "0.4.5"
|
||||
resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-0.4.5.tgz#5fbcd87cd63a5c49a7fce2fe56f425e05729c68c"
|
||||
integrity sha1-X7zYfNY6XEmn/OL+VvQl4Fcpxow=
|
||||
dependencies:
|
||||
es6-templates "^0.2.2"
|
||||
fastparse "^1.1.1"
|
||||
html-minifier "^3.0.1"
|
||||
loader-utils "^1.0.2"
|
||||
object-assign "^4.1.0"
|
||||
|
||||
html-loader@^0.5.5:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-0.5.5.tgz#6356dbeb0c49756d8ebd5ca327f16ff06ab5faea"
|
||||
|
@ -7887,7 +7961,7 @@ html-loader@^0.5.5:
|
|||
loader-utils "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
html-minifier@^3.2.3, html-minifier@^3.5.8:
|
||||
html-minifier@^3.0.1, html-minifier@^3.2.3, html-minifier@^3.5.8:
|
||||
version "3.5.21"
|
||||
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
|
||||
integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
|
||||
|
@ -8110,6 +8184,11 @@ ignore@^4.0.6:
|
|||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||
|
||||
immediate@~3.0.5:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
|
||||
|
||||
immutable@3.7.6:
|
||||
version "3.7.6"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
|
||||
|
@ -8699,6 +8778,13 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
|
||||
|
||||
is-plain-object@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.1.tgz#4d7ca539bc9db9b737b8acb612f2318ef92f294f"
|
||||
integrity sha1-TXylObydubc3uKy2EvIxjvkvKU8=
|
||||
dependencies:
|
||||
isobject "^1.0.0"
|
||||
|
||||
is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||
|
@ -8831,6 +8917,11 @@ isexe@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
||||
|
||||
isobject@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-1.0.2.tgz#f0f9b8ce92dd540fa0740882e3835a2e022ec78a"
|
||||
integrity sha1-8Pm4zpLdVA+gdAiC44NaLgIux4o=
|
||||
|
||||
isobject@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
|
||||
|
@ -8985,7 +9076,7 @@ jade@0.26.3:
|
|||
commander "0.6.1"
|
||||
mkdirp "0.3.0"
|
||||
|
||||
javascript-stringify@^1.1.0, javascript-stringify@^1.2.0:
|
||||
javascript-stringify@^1.1.0, javascript-stringify@^1.2.0, javascript-stringify@^1.5.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3"
|
||||
integrity sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=
|
||||
|
@ -9598,7 +9689,7 @@ js-yaml@~3.7.0:
|
|||
argparse "^1.0.7"
|
||||
esprima "^2.6.0"
|
||||
|
||||
jsan@^3.1.13, jsan@^3.1.3:
|
||||
jsan@^3.1.13, jsan@^3.1.3, jsan@^3.1.9:
|
||||
version "3.1.13"
|
||||
resolved "https://registry.yarnpkg.com/jsan/-/jsan-3.1.13.tgz#4de8c7bf8d1cfcd020c313d438f930cec4b91d86"
|
||||
integrity sha512-9kGpCsGHifmw6oJet+y8HaCl14y7qgAsxVdV3pCHDySNR3BfDC30zgkssd7x5LRVAT22dnpbe9JdzzmXZnq9/g==
|
||||
|
@ -9989,6 +10080,18 @@ libnpmaccess@^3.0.0:
|
|||
npm-package-arg "^6.1.0"
|
||||
npm-registry-fetch "^3.8.0"
|
||||
|
||||
lie@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
linked-list@0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/linked-list/-/linked-list-0.1.0.tgz#798b0ff97d1b92a4fd08480f55aea4e9d49d37bf"
|
||||
integrity sha1-eYsP+X0bkqT9CEgPVa6k6dSdN78=
|
||||
|
||||
lint-staged@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-4.3.0.tgz#ed0779ad9a42c0dc62bb3244e522870b41125879"
|
||||
|
@ -10133,6 +10236,13 @@ loader-utils@^1.1.0:
|
|||
emojis-list "^2.0.0"
|
||||
json5 "^1.0.1"
|
||||
|
||||
localforage@^1.5.0:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.7.3.tgz#0082b3ca9734679e1bd534995bdd3b24cf10f204"
|
||||
integrity sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==
|
||||
dependencies:
|
||||
lie "3.1.1"
|
||||
|
||||
locate-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
|
||||
|
@ -10149,7 +10259,7 @@ locate-path@^3.0.0:
|
|||
p-locate "^3.0.0"
|
||||
path-exists "^3.0.0"
|
||||
|
||||
lodash-es@^4.2.1:
|
||||
lodash-es@^4.17.4, lodash-es@^4.2.1:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0"
|
||||
integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==
|
||||
|
@ -12111,7 +12221,7 @@ p-is-promise@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e"
|
||||
integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=
|
||||
|
||||
p-limit@^1.1.0:
|
||||
p-limit@^1.0.0, p-limit@^1.1.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
|
||||
integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
|
||||
|
@ -13499,7 +13609,7 @@ react-icon-base@2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/react-icon-base/-/react-icon-base-2.1.0.tgz#a196e33fdf1e7aaa1fda3aefbb68bdad9e82a79d"
|
||||
integrity sha1-oZbjP98eeqof2jrvu2i9rZ6Cp50=
|
||||
|
||||
react-icons@^2.2.3, react-icons@^2.2.7:
|
||||
react-icons@^2.2.3, react-icons@^2.2.5, react-icons@^2.2.7:
|
||||
version "2.2.7"
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-2.2.7.tgz#d7860826b258557510dac10680abea5ca23cf650"
|
||||
integrity sha512-0n4lcGqzJFcIQLoQytLdJCE0DKSA9dkwEZRYoGrIDJZFvIT6Hbajx5mv9geqhqFiNjUgtxg8kPyDfjlhymbGFg==
|
||||
|
@ -13627,7 +13737,7 @@ react-pure-render@^1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/react-pure-render/-/react-pure-render-1.0.2.tgz#9d8a928c7f2c37513c2d064e57b3e3c356e9fabb"
|
||||
integrity sha1-nYqSjH8sN1E8LQZOV7Pjw1bp+rs=
|
||||
|
||||
react-redux@^5.0.2:
|
||||
react-redux@^5.0.2, react-redux@^5.0.5:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f"
|
||||
integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==
|
||||
|
@ -13697,6 +13807,14 @@ react-style-proptype@^3.0.0:
|
|||
dependencies:
|
||||
prop-types "^15.5.4"
|
||||
|
||||
react-test-renderer@^15.5.4:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.6.2.tgz#d0333434fc2c438092696ca770da5ed48037efa8"
|
||||
integrity sha1-0DM0NPwsQ4CSaWyncNpe1IA376g=
|
||||
dependencies:
|
||||
fbjs "^0.8.9"
|
||||
object-assign "^4.1.0"
|
||||
|
||||
react-test-renderer@^16.0.0, react-test-renderer@^16.0.0-0, react-test-renderer@^16.4.0:
|
||||
version "16.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.7.0.tgz#1ca96c2b450ab47c36ba92cd8c03fcefc52ea01c"
|
||||
|
@ -14037,6 +14155,17 @@ reduce-function-call@^1.0.1:
|
|||
dependencies:
|
||||
balanced-match "^0.4.2"
|
||||
|
||||
redux-devtools-chart-monitor@^1.6.1:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/redux-devtools-chart-monitor/-/redux-devtools-chart-monitor-1.7.0.tgz#bf6356f480142e3576f5bbbeead433a1598c5e7f"
|
||||
integrity sha512-1knxXASbo7ukukyf1rGNnME7gOgKY1XVZ4hoSzUjY6QFIC8iEneivXznCupxjfX5TDXLjZgQrFBrbGano1WK7g==
|
||||
dependencies:
|
||||
d3-state-visualizer "^1.3.1"
|
||||
deepmerge "^0.2.10"
|
||||
prop-types "^15.6.0"
|
||||
react-pure-render "^1.0.2"
|
||||
redux-devtools-themes "^1.0.0"
|
||||
|
||||
redux-devtools-dock-monitor@^1.0.1, redux-devtools-dock-monitor@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/redux-devtools-dock-monitor/-/redux-devtools-dock-monitor-1.1.3.tgz#1205e823c82536570aac8551a1c4b70972cba6aa"
|
||||
|
@ -14055,14 +14184,31 @@ redux-devtools-themes@^1.0.0:
|
|||
dependencies:
|
||||
base16 "^1.0.0"
|
||||
|
||||
redux-logger@^2.5.2, redux-logger@^2.8.1:
|
||||
redux-immutable-state-invariant@^1.2.0:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/redux-immutable-state-invariant/-/redux-immutable-state-invariant-1.2.4.tgz#e8bc4a37e22815375d5a04f8ecbb807054ea8bbb"
|
||||
integrity sha1-6LxKN+IoFTddWgT47LuAcFTqi7s=
|
||||
dependencies:
|
||||
invariant "^2.1.0"
|
||||
json-stringify-safe "^5.0.1"
|
||||
|
||||
redux-logger@^2.2.1, redux-logger@^2.5.2, redux-logger@^2.8.1:
|
||||
version "2.10.2"
|
||||
resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-2.10.2.tgz#3c5a5f0a6f32577c1deadf6655f257f82c6c3937"
|
||||
integrity sha1-PFpfCm8yV3wd6t9mVfJX+CxsOTc=
|
||||
dependencies:
|
||||
deep-diff "0.3.4"
|
||||
|
||||
redux@^3.6.0, redux@^3.7.2:
|
||||
redux-persist@^4.8.0:
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-4.10.2.tgz#8efdb16cfe882c521a78a6d0bfdfef2437f49f96"
|
||||
integrity sha512-U+e0ieMGC69Zr72929iJW40dEld7Mflh6mu0eJtVMLGfMq/aJqjxUM1hzyUWMR1VUyAEEdPHuQmeq5ti9krIgg==
|
||||
dependencies:
|
||||
json-stringify-safe "^5.0.1"
|
||||
lodash "^4.17.4"
|
||||
lodash-es "^4.17.4"
|
||||
|
||||
redux@^3.0.5, redux@^3.6.0, redux@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
|
||||
integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==
|
||||
|
@ -14551,6 +14697,11 @@ safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s
|
|||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@~5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
|
||||
integrity sha1-0mPKVGls2KMGtcplUekt5XkY++c=
|
||||
|
||||
safe-regex@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
|
||||
|
@ -14603,6 +14754,30 @@ sax@^1.2.1, sax@^1.2.4, sax@~1.2.1:
|
|||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
sc-channel@~1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/sc-channel/-/sc-channel-1.0.6.tgz#b38bd47a993e78290fbc53467867f6b2a0a08639"
|
||||
integrity sha1-s4vUepk+eCkPvFNGeGf2sqCghjk=
|
||||
dependencies:
|
||||
sc-emitter "1.x.x"
|
||||
|
||||
sc-emitter@1.x.x, sc-emitter@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/sc-emitter/-/sc-emitter-1.1.0.tgz#ef119d4222f4c64f887b486964ef11116cdd0e75"
|
||||
integrity sha1-7xGdQiL0xk+Ie0hpZO8REWzdDnU=
|
||||
dependencies:
|
||||
component-emitter "1.2.0"
|
||||
|
||||
sc-errors@~1.3.0:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/sc-errors/-/sc-errors-1.3.3.tgz#c00bc4c766a970cc8d5937d08cd58e931d7dae05"
|
||||
integrity sha1-wAvEx2apcMyNWTfQjNWOkx19rgU=
|
||||
|
||||
sc-formatter@~3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/sc-formatter/-/sc-formatter-3.0.2.tgz#9abdb14e71873ce7157714d3002477bbdb33c4e6"
|
||||
integrity sha512-9PbqYBpCq+OoEeRQ3QfFIGE6qwjjBcd2j7UjgDlhnZbtSnuGgHdcRklPKYGuYFH82V/dwd+AIpu8XvA1zqTd+A==
|
||||
|
||||
scheduler@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b"
|
||||
|
@ -14952,6 +15127,21 @@ snapdragon@^0.8.1:
|
|||
source-map-resolve "^0.5.0"
|
||||
use "^3.1.0"
|
||||
|
||||
socketcluster-client@^5.5.0:
|
||||
version "5.5.2"
|
||||
resolved "https://registry.yarnpkg.com/socketcluster-client/-/socketcluster-client-5.5.2.tgz#9d4369e0e722ff7e55e5422c2d44f5afe1aff128"
|
||||
integrity sha1-nUNp4Oci/35V5UIsLUT1r+Gv8Sg=
|
||||
dependencies:
|
||||
base-64 "0.1.0"
|
||||
clone "2.1.1"
|
||||
linked-list "0.1.0"
|
||||
querystring "0.2.0"
|
||||
sc-channel "~1.0.6"
|
||||
sc-emitter "~1.1.0"
|
||||
sc-errors "~1.3.0"
|
||||
sc-formatter "~3.0.0"
|
||||
ws "3.0.0"
|
||||
|
||||
sockjs-client@1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83"
|
||||
|
@ -15539,7 +15729,7 @@ strong-log-transformer@^2.0.0:
|
|||
minimist "^1.2.0"
|
||||
through "^2.3.4"
|
||||
|
||||
style-loader@^0.13.1:
|
||||
style-loader@^0.13.0, style-loader@^0.13.1:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.2.tgz#74533384cf698c7104c7951150b49717adc2f3bb"
|
||||
integrity sha1-dFMzhM9pjHEEx5URULSXF63C87s=
|
||||
|
@ -15574,7 +15764,7 @@ style-search@^0.1.0:
|
|||
resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902"
|
||||
integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=
|
||||
|
||||
styled-components@^2.2.2:
|
||||
styled-components@^2.0.0, styled-components@^2.2.2:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.4.1.tgz#663bd0485d4b6ab46f946210dc03d2398d1ade74"
|
||||
integrity sha1-ZjvQSF1LarRvlGIQ3APSOY0a3nQ=
|
||||
|
@ -16286,6 +16476,11 @@ uid-number@0.0.6:
|
|||
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
|
||||
integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=
|
||||
|
||||
ultron@~1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
|
||||
integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==
|
||||
|
||||
umask@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
|
||||
|
@ -16699,7 +16894,7 @@ webpack-dev-server@^2.3.0, webpack-dev-server@^2.4.1:
|
|||
webpack-dev-middleware "1.12.2"
|
||||
yargs "6.6.0"
|
||||
|
||||
webpack-hot-middleware@^2.22.1:
|
||||
webpack-hot-middleware@^2.16.1, webpack-hot-middleware@^2.22.1:
|
||||
version "2.24.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.24.3.tgz#5bb76259a8fc0d97463ab517640ba91d3382d4a6"
|
||||
integrity sha512-pPlmcdoR2Fn6UhYjAhp1g/IJy1Yc9hD+T6O9mjRcWV2pFbBjIFoJXhP0CoD0xPOhWJuWXuZXGBga9ybbOdzXpg==
|
||||
|
@ -16999,6 +17194,14 @@ write@^0.2.1:
|
|||
dependencies:
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
ws@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-3.0.0.tgz#98ddb00056c8390cb751e7788788497f99103b6c"
|
||||
integrity sha1-mN2wAFbIOQy3Ued4h4hJf5kQO2w=
|
||||
dependencies:
|
||||
safe-buffer "~5.0.1"
|
||||
ultron "~1.1.0"
|
||||
|
||||
ws@^5.2.0:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
|
||||
|
|
Loading…
Reference in New Issue
Block a user