mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 00:19:55 +03:00
Convert redux-devtools-inspector to TypeScript
This commit is contained in:
parent
93d6bc4350
commit
0d7eab049b
|
@ -1,9 +1,10 @@
|
||||||
{
|
{
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
"presets": [
|
||||||
|
"@babel/env",
|
||||||
|
"@babel/react",
|
||||||
|
"@babel/typescript"
|
||||||
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@babel/plugin-transform-runtime",
|
"@babel/proposal-class-properties"
|
||||||
"@babel/plugin-proposal-class-properties",
|
|
||||||
"@babel/plugin-proposal-export-default-from",
|
|
||||||
"@babel/plugin-proposal-do-expressions"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
2
packages/redux-devtools-inspector/.eslintignore
Normal file
2
packages/redux-devtools-inspector/.eslintignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
lib
|
||||||
|
demo
|
13
packages/redux-devtools-inspector/.eslintrc.js
Normal file
13
packages/redux-devtools-inspector/.eslintrc.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: '../../.eslintrc',
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
extends: '../../eslintrc.ts.react.base.json',
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ['./tsconfig.json']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
|
@ -1,8 +0,0 @@
|
||||||
static
|
|
||||||
src
|
|
||||||
demo
|
|
||||||
.*
|
|
||||||
webpack.config.js
|
|
||||||
index.html
|
|
||||||
*.gif
|
|
||||||
*.png
|
|
2
packages/redux-devtools-inspector/.prettierignore
Normal file
2
packages/redux-devtools-inspector/.prettierignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
lib
|
||||||
|
demo
|
|
@ -3,19 +3,25 @@
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"description": "Redux DevTools Diff Monitor",
|
"description": "Redux DevTools Diff Monitor",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:lib",
|
"type-check": "tsc --noEmit",
|
||||||
"build:lib": "cross-env NODE_ENV=production babel src --out-dir lib",
|
"type-check:watch": "npm run type-check -- --watch",
|
||||||
|
"build": "npm run build:types && npm run build:js",
|
||||||
|
"build:types": "tsc --emitDeclarationOnly",
|
||||||
|
"build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline",
|
||||||
|
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||||
|
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
||||||
"build:demo": "cross-env NODE_ENV=production webpack -p",
|
"build:demo": "cross-env NODE_ENV=production webpack -p",
|
||||||
"stats": "webpack --profile --json > stats.json",
|
"stats": "webpack --profile --json > stats.json",
|
||||||
"start": "webpack-dev-server",
|
"start": "webpack-dev-server",
|
||||||
"preversion": "npm run lint",
|
"preversion": "npm run lint",
|
||||||
"version": "npm run build:demo && git add -A .",
|
"version": "npm run build:demo && git add -A .",
|
||||||
"postversion": "git push",
|
"postversion": "git push",
|
||||||
"prepare": "npm run build:lib",
|
"prepare": "npm run build",
|
||||||
"prepublishOnly": "npm run build:lib",
|
"prepublishOnly": "npm run build",
|
||||||
"gh": "git subtree push --prefix demo/dist origin gh-pages"
|
"gh": "git subtree push --prefix demo/dist origin gh-pages"
|
||||||
},
|
},
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
"types": "lib/index.d.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/reduxjs/redux-devtools"
|
"url": "https://github.com/reduxjs/redux-devtools"
|
||||||
},
|
},
|
||||||
|
@ -28,11 +34,17 @@
|
||||||
"@babel/plugin-transform-runtime": "^7.2.0",
|
"@babel/plugin-transform-runtime": "^7.2.0",
|
||||||
"@babel/preset-env": "^7.3.1",
|
"@babel/preset-env": "^7.3.1",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
|
"@types/dateformat": "^3.0.1",
|
||||||
|
"@types/dragula": "^3.7.0",
|
||||||
|
"@types/hex-rgba": "^1.0.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^2.31.0",
|
||||||
|
"@typescript-eslint/parser": "^2.31.0",
|
||||||
"babel-loader": "^8.0.5",
|
"babel-loader": "^8.0.5",
|
||||||
"base16": "^1.0.0",
|
"base16": "^1.0.0",
|
||||||
"chokidar": "^1.6.1",
|
"chokidar": "^1.6.1",
|
||||||
"clean-webpack-plugin": "^1.0.0",
|
"clean-webpack-plugin": "^1.0.0",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
|
"csstype": "^2.6.10",
|
||||||
"export-files-webpack-plugin": "0.0.1",
|
"export-files-webpack-plugin": "0.0.1",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"lodash.shuffle": "^4.2.0",
|
"lodash.shuffle": "^4.2.0",
|
||||||
|
@ -50,13 +62,14 @@
|
||||||
"redux-devtools": "^3.1.0",
|
"redux-devtools": "^3.1.0",
|
||||||
"redux-devtools-dock-monitor": "^1.0.1",
|
"redux-devtools-dock-monitor": "^1.0.1",
|
||||||
"redux-logger": "^2.5.2",
|
"redux-logger": "^2.5.2",
|
||||||
|
"typescript": "^3.8.3",
|
||||||
"webpack": "^4.27.1",
|
"webpack": "^4.27.1",
|
||||||
"webpack-cli": "^3.2.0",
|
"webpack-cli": "^3.2.0",
|
||||||
"webpack-dev-server": "^3.1.14"
|
"webpack-dev-server": "^3.1.14"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=15.0.0",
|
"@types/react": "^16.3.0",
|
||||||
"react-dom": ">=15.0.0"
|
"react": "^16.3.0"
|
||||||
},
|
},
|
||||||
"author": "Alexander <alexkuz@gmail.com> (http://kuzya.org/)",
|
"author": "Alexander <alexkuz@gmail.com> (http://kuzya.org/)",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
|
@ -64,6 +77,7 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/prop-types": "^15.6.2",
|
||||||
"babel-runtime": "^6.3.19",
|
"babel-runtime": "^6.3.19",
|
||||||
"dateformat": "^1.0.12",
|
"dateformat": "^1.0.12",
|
||||||
"hex-rgba": "^1.0.0",
|
"hex-rgba": "^1.0.0",
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"import": true
|
|
||||||
}
|
|
|
@ -1,10 +1,18 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component, RefCallback } from 'react';
|
||||||
import dragula from 'react-dragula';
|
import dragula from 'react-dragula';
|
||||||
import ActionListRow from './ActionListRow';
|
import ActionListRow from './ActionListRow';
|
||||||
import ActionListHeader from './ActionListHeader';
|
import ActionListHeader from './ActionListHeader';
|
||||||
import shouldPureComponentUpdate from 'react-pure-render/function';
|
import shouldPureComponentUpdate from 'react-pure-render/function';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { PerformAction } from 'redux-devtools';
|
||||||
|
import { StylingFunction } from 'react-base16-styling';
|
||||||
|
import { Drake } from 'dragula';
|
||||||
|
|
||||||
function getTimestamps(actions, actionIds, actionId) {
|
function getTimestamps<A extends Action<unknown>>(
|
||||||
|
actions: { [actionId: number]: PerformAction<A> },
|
||||||
|
actionIds: number[],
|
||||||
|
actionId: number
|
||||||
|
) {
|
||||||
const idx = actionIds.indexOf(actionId);
|
const idx = actionIds.indexOf(actionId);
|
||||||
const prevActionId = actionIds[idx - 1];
|
const prevActionId = actionIds[idx - 1];
|
||||||
|
|
||||||
|
@ -14,10 +22,40 @@ function getTimestamps(actions, actionIds, actionId) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ActionList extends Component {
|
interface Props<S, A extends Action<unknown>> {
|
||||||
|
actions: { [actionId: number]: PerformAction<A> };
|
||||||
|
actionIds: number[];
|
||||||
|
isWideLayout: boolean;
|
||||||
|
searchValue: string | undefined;
|
||||||
|
selectedActionId: number | null;
|
||||||
|
startActionId: number | null;
|
||||||
|
skippedActionIds: number[];
|
||||||
|
draggableActions: boolean;
|
||||||
|
hideMainButtons: boolean | undefined;
|
||||||
|
hideActionButtons: boolean | undefined;
|
||||||
|
styling: StylingFunction;
|
||||||
|
|
||||||
|
onSelect: (event: React.MouseEvent<HTMLDivElement>, actionId: number) => void;
|
||||||
|
onSearch: (value: string) => void;
|
||||||
|
onToggleAction: (actionId: number) => void;
|
||||||
|
onJumpToState: (actionId: number) => void;
|
||||||
|
onCommit: () => void;
|
||||||
|
onSweep: () => void;
|
||||||
|
onReorderAction: (actionId: number, beforeActionId: number) => void;
|
||||||
|
currentActionId: number;
|
||||||
|
lastActionId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ActionList<S, A extends Action<unknown>> extends Component<
|
||||||
|
Props<S, A>
|
||||||
|
> {
|
||||||
shouldComponentUpdate = shouldPureComponentUpdate;
|
shouldComponentUpdate = shouldPureComponentUpdate;
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
node?: HTMLDivElement | null;
|
||||||
|
scrollDown?: boolean;
|
||||||
|
drake?: Drake;
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps: Props<S, A>) {
|
||||||
const node = this.node;
|
const node = this.node;
|
||||||
if (!node) {
|
if (!node) {
|
||||||
this.scrollDown = true;
|
this.scrollDown = true;
|
||||||
|
@ -35,22 +73,22 @@ export default class ActionList extends Component {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
|
|
||||||
if (!this.props.draggableActions) return;
|
if (!this.props.draggableActions) return;
|
||||||
const container = this.node;
|
const container = this.node!;
|
||||||
this.drake = dragula([container], {
|
this.drake = dragula([container], {
|
||||||
copy: false,
|
copy: false,
|
||||||
copySortSource: false,
|
copySortSource: false,
|
||||||
mirrorContainer: container,
|
mirrorContainer: container,
|
||||||
accepts: (el, target, source, sibling) =>
|
accepts: (el, target, source, sibling) =>
|
||||||
!sibling || parseInt(sibling.getAttribute('data-id')),
|
!sibling || !!parseInt(sibling.getAttribute('data-id')!),
|
||||||
moves: (el, source, handle) =>
|
moves: (el, source, handle) =>
|
||||||
parseInt(el.getAttribute('data-id')) &&
|
!!parseInt(el!.getAttribute('data-id')!) &&
|
||||||
handle.className.indexOf('selectorButton') !== 0
|
!handle!.className.startsWith('selectorButton')
|
||||||
}).on('drop', (el, target, source, sibling) => {
|
}).on('drop', (el, target, source, sibling) => {
|
||||||
let beforeActionId = this.props.actionIds.length;
|
let beforeActionId = this.props.actionIds.length;
|
||||||
if (sibling && sibling.className.indexOf('gu-mirror') === -1) {
|
if (sibling && !sibling.className.includes('gu-mirror')) {
|
||||||
beforeActionId = parseInt(sibling.getAttribute('data-id'));
|
beforeActionId = parseInt(sibling.getAttribute('data-id')!);
|
||||||
}
|
}
|
||||||
const actionId = parseInt(el.getAttribute('data-id'));
|
const actionId = parseInt(el.getAttribute('data-id')!);
|
||||||
this.props.onReorderAction(actionId, beforeActionId);
|
this.props.onReorderAction(actionId, beforeActionId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -69,7 +107,7 @@ export default class ActionList extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getRef = node => {
|
getRef: RefCallback<HTMLDivElement> = node => {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,10 +133,10 @@ export default class ActionList extends Component {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const lowerSearchValue = searchValue && searchValue.toLowerCase();
|
const lowerSearchValue = searchValue && searchValue.toLowerCase();
|
||||||
const filteredActionIds = searchValue
|
const filteredActionIds = searchValue
|
||||||
? actionIds.filter(
|
? actionIds.filter(id =>
|
||||||
id =>
|
(actions[id].action.type as string)
|
||||||
actions[id].action.type.toLowerCase().indexOf(lowerSearchValue) !==
|
.toLowerCase()
|
||||||
-1
|
.includes(lowerSearchValue as string)
|
||||||
)
|
)
|
||||||
: actionIds;
|
: actionIds;
|
||||||
|
|
||||||
|
@ -129,20 +167,22 @@ export default class ActionList extends Component {
|
||||||
isSelected={
|
isSelected={
|
||||||
(startActionId !== null &&
|
(startActionId !== null &&
|
||||||
actionId >= startActionId &&
|
actionId >= startActionId &&
|
||||||
actionId <= selectedActionId) ||
|
actionId <= (selectedActionId as number)) ||
|
||||||
actionId === selectedActionId
|
actionId === selectedActionId
|
||||||
}
|
}
|
||||||
isInFuture={
|
isInFuture={
|
||||||
actionIds.indexOf(actionId) > actionIds.indexOf(currentActionId)
|
actionIds.indexOf(actionId) > actionIds.indexOf(currentActionId)
|
||||||
}
|
}
|
||||||
onSelect={e => onSelect(e, actionId)}
|
onSelect={(e: React.MouseEvent<HTMLDivElement>) =>
|
||||||
|
onSelect(e, actionId)
|
||||||
|
}
|
||||||
timestamps={getTimestamps(actions, actionIds, actionId)}
|
timestamps={getTimestamps(actions, actionIds, actionId)}
|
||||||
action={actions[actionId].action}
|
action={actions[actionId].action}
|
||||||
onToggleClick={() => onToggleAction(actionId)}
|
onToggleClick={() => onToggleAction(actionId)}
|
||||||
onJumpClick={() => onJumpToState(actionId)}
|
onJumpClick={() => onJumpToState(actionId)}
|
||||||
onCommitClick={() => onCommit(actionId)}
|
onCommitClick={() => onCommit()}
|
||||||
hideActionButtons={hideActionButtons}
|
hideActionButtons={hideActionButtons}
|
||||||
isSkipped={skippedActionIds.indexOf(actionId) !== -1}
|
isSkipped={skippedActionIds.includes(actionId)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
|
@ -1,10 +1,25 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import RightSlider from './RightSlider';
|
import RightSlider from './RightSlider';
|
||||||
|
import { StylingFunction } from 'react-base16-styling';
|
||||||
|
|
||||||
const getActiveButtons = hasSkippedActions =>
|
const getActiveButtons = (hasSkippedActions: boolean): ('Sweep' | 'Commit')[] =>
|
||||||
[hasSkippedActions && 'Sweep', 'Commit'].filter(a => a);
|
[hasSkippedActions && 'Sweep', 'Commit'].filter(a => a) as (
|
||||||
|
| 'Sweep'
|
||||||
|
| 'Commit'
|
||||||
|
)[];
|
||||||
|
|
||||||
const ActionListHeader = ({
|
interface Props {
|
||||||
|
styling: StylingFunction;
|
||||||
|
onSearch: (value: string) => void;
|
||||||
|
hasSkippedActions: boolean;
|
||||||
|
hasStagedActions: boolean;
|
||||||
|
onCommit: () => void;
|
||||||
|
onSweep: () => void;
|
||||||
|
hideMainButtons: boolean | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionListHeader: React.FunctionComponent<Props> = ({
|
||||||
styling,
|
styling,
|
||||||
onSearch,
|
onSearch,
|
||||||
hasSkippedActions,
|
hasSkippedActions,
|
||||||
|
@ -48,4 +63,14 @@ const ActionListHeader = ({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ActionListHeader.propTypes = {
|
||||||
|
styling: PropTypes.func.isRequired,
|
||||||
|
onSearch: PropTypes.func.isRequired,
|
||||||
|
hasSkippedActions: PropTypes.bool.isRequired,
|
||||||
|
hasStagedActions: PropTypes.bool.isRequired,
|
||||||
|
onCommit: PropTypes.func.isRequired,
|
||||||
|
onSweep: PropTypes.func.isRequired,
|
||||||
|
hideMainButtons: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
export default ActionListHeader;
|
export default ActionListHeader;
|
|
@ -1,14 +1,41 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { PropTypes } from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import shouldPureComponentUpdate from 'react-pure-render/function';
|
import shouldPureComponentUpdate from 'react-pure-render/function';
|
||||||
import dateformat from 'dateformat';
|
import dateformat from 'dateformat';
|
||||||
import debounce from 'lodash.debounce';
|
import debounce from 'lodash.debounce';
|
||||||
import RightSlider from './RightSlider';
|
import RightSlider from './RightSlider';
|
||||||
|
import { StylingFunction } from 'react-base16-styling';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
const BUTTON_SKIP = 'Skip';
|
const BUTTON_SKIP = 'Skip';
|
||||||
const BUTTON_JUMP = 'Jump';
|
const BUTTON_JUMP = 'Jump';
|
||||||
|
|
||||||
export default class ActionListRow extends Component {
|
type Button = typeof BUTTON_SKIP | typeof BUTTON_JUMP;
|
||||||
|
|
||||||
|
interface Props<A extends Action<unknown>> {
|
||||||
|
styling: StylingFunction;
|
||||||
|
isSelected: boolean;
|
||||||
|
action: A;
|
||||||
|
actionId: number;
|
||||||
|
isInitAction: boolean;
|
||||||
|
onSelect: React.MouseEventHandler<HTMLDivElement> | undefined;
|
||||||
|
timestamps: { current: number; previous: number };
|
||||||
|
isSkipped: boolean;
|
||||||
|
isInFuture: boolean;
|
||||||
|
hideActionButtons: boolean | undefined;
|
||||||
|
onToggleClick: () => void;
|
||||||
|
onJumpClick: () => void;
|
||||||
|
onCommitClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
hover: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ActionListRow<A extends Action<unknown>> extends Component<
|
||||||
|
Props<A>,
|
||||||
|
State
|
||||||
|
> {
|
||||||
state = { hover: false };
|
state = { hover: false };
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -44,18 +71,24 @@ export default class ActionListRow extends Component {
|
||||||
const timeDelta = timestamps.current - timestamps.previous;
|
const timeDelta = timestamps.current - timestamps.previous;
|
||||||
const showButtons = (hover && !isInitAction) || isSkipped;
|
const showButtons = (hover && !isInitAction) || isSkipped;
|
||||||
|
|
||||||
const isButtonSelected = btn => btn === BUTTON_SKIP && isSkipped;
|
const isButtonSelected = (btn: Button) => btn === BUTTON_SKIP && isSkipped;
|
||||||
|
|
||||||
let actionType = action.type;
|
let actionType = action.type;
|
||||||
if (typeof actionType === 'undefined') actionType = '<UNDEFINED>';
|
if (typeof actionType === 'undefined') actionType = '<UNDEFINED>';
|
||||||
else if (actionType === null) actionType = '<NULL>';
|
else if (actionType === null) actionType = '<NULL>';
|
||||||
else actionType = actionType.toString() || '<EMPTY>';
|
else actionType = (actionType as string).toString() || '<EMPTY>';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={onSelect}
|
onClick={onSelect}
|
||||||
onMouseEnter={!hideActionButtons && this.handleMouseEnter}
|
onMouseEnter={
|
||||||
onMouseLeave={!hideActionButtons && this.handleMouseLeave}
|
(!hideActionButtons &&
|
||||||
|
this.handleMouseEnter) as React.MouseEventHandler<HTMLDivElement>
|
||||||
|
}
|
||||||
|
onMouseLeave={
|
||||||
|
(!hideActionButtons &&
|
||||||
|
this.handleMouseLeave) as React.MouseEventHandler<HTMLDivElement>
|
||||||
|
}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
onMouseUp={this.handleMouseEnter}
|
onMouseUp={this.handleMouseEnter}
|
||||||
data-id={actionId}
|
data-id={actionId}
|
||||||
|
@ -76,7 +109,7 @@ export default class ActionListRow extends Component {
|
||||||
isSkipped && 'actionListItemNameSkipped'
|
isSkipped && 'actionListItemNameSkipped'
|
||||||
])}
|
])}
|
||||||
>
|
>
|
||||||
{actionType}
|
{actionType as string}
|
||||||
</div>
|
</div>
|
||||||
{hideActionButtons ? (
|
{hideActionButtons ? (
|
||||||
<RightSlider styling={styling} shown>
|
<RightSlider styling={styling} shown>
|
||||||
|
@ -103,12 +136,12 @@ export default class ActionListRow extends Component {
|
||||||
</RightSlider>
|
</RightSlider>
|
||||||
<RightSlider styling={styling} shown={showButtons} rotate>
|
<RightSlider styling={styling} shown={showButtons} rotate>
|
||||||
<div {...styling('actionListItemSelector')}>
|
<div {...styling('actionListItemSelector')}>
|
||||||
{[BUTTON_JUMP, BUTTON_SKIP].map(
|
{([BUTTON_JUMP, BUTTON_SKIP] as const).map(
|
||||||
btn =>
|
btn =>
|
||||||
(!isInitAction || btn !== BUTTON_SKIP) && (
|
(!isInitAction || btn !== BUTTON_SKIP) && (
|
||||||
<div
|
<div
|
||||||
key={btn}
|
key={btn}
|
||||||
onClick={this.handleButtonClick.bind(this, btn)}
|
onClick={e => this.handleButtonClick(btn, e)}
|
||||||
{...styling(
|
{...styling(
|
||||||
[
|
[
|
||||||
'selectorButton',
|
'selectorButton',
|
||||||
|
@ -131,7 +164,7 @@ export default class ActionListRow extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleButtonClick(btn, e) {
|
handleButtonClick = (btn: Button, e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
switch (btn) {
|
switch (btn) {
|
||||||
|
@ -142,10 +175,10 @@ export default class ActionListRow extends Component {
|
||||||
this.props.onJumpClick();
|
this.props.onJumpClick();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMouseEnter = e => {
|
handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (this.hover) return;
|
if (this.state.hover) return;
|
||||||
this.handleMouseLeave.cancel();
|
this.handleMouseLeave.cancel();
|
||||||
this.handleMouseEnterDebounced(e.buttons);
|
this.handleMouseEnterDebounced(e.buttons);
|
||||||
};
|
};
|
||||||
|
@ -160,8 +193,13 @@ export default class ActionListRow extends Component {
|
||||||
if (this.state.hover) this.setState({ hover: false });
|
if (this.state.hover) this.setState({ hover: false });
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
handleMouseDown = e => {
|
handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (e.target.className.indexOf('selectorButton') === 0) return;
|
if (
|
||||||
|
((e.target as unknown) as { className: string[] }).className.indexOf(
|
||||||
|
'selectorButton'
|
||||||
|
) === 0
|
||||||
|
)
|
||||||
|
return;
|
||||||
this.handleMouseLeave();
|
this.handleMouseLeave();
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -1,9 +1,41 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { DEFAULT_STATE } from './redux';
|
import { DEFAULT_STATE, MonitorState } from './redux';
|
||||||
import ActionPreviewHeader from './ActionPreviewHeader';
|
import ActionPreviewHeader from './ActionPreviewHeader';
|
||||||
import DiffTab from './tabs/DiffTab';
|
import DiffTab from './tabs/DiffTab';
|
||||||
import StateTab from './tabs/StateTab';
|
import StateTab from './tabs/StateTab';
|
||||||
import ActionTab from './tabs/ActionTab';
|
import ActionTab from './tabs/ActionTab';
|
||||||
|
import { Base16Theme, StylingFunction } from 'react-base16-styling';
|
||||||
|
import { Delta } from 'jsondiffpatch';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { PerformAction } from 'redux-devtools';
|
||||||
|
|
||||||
|
export interface TabComponentProps<S, A extends Action<unknown>> {
|
||||||
|
labelRenderer: (
|
||||||
|
keyPath: (string | number)[],
|
||||||
|
nodeType: string,
|
||||||
|
expanded: boolean,
|
||||||
|
expandable: boolean
|
||||||
|
) => React.ReactNode;
|
||||||
|
styling: StylingFunction;
|
||||||
|
computedStates: { state: S; error?: string }[];
|
||||||
|
actions: { [actionId: number]: PerformAction<A> };
|
||||||
|
selectedActionId: number | null;
|
||||||
|
startActionId: number | null;
|
||||||
|
base16Theme: Base16Theme;
|
||||||
|
invertTheme: boolean;
|
||||||
|
isWideLayout: boolean;
|
||||||
|
dataTypeKey: string | undefined;
|
||||||
|
delta: Delta | null | undefined | false;
|
||||||
|
action: A;
|
||||||
|
nextState: S;
|
||||||
|
monitorState: MonitorState;
|
||||||
|
updateMonitorState: (monitorState: Partial<MonitorState>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tab<S, A extends Action<unknown>> {
|
||||||
|
name: string;
|
||||||
|
component: React.ComponentType<TabComponentProps<S, A>>;
|
||||||
|
}
|
||||||
|
|
||||||
const DEFAULT_TABS = [
|
const DEFAULT_TABS = [
|
||||||
{
|
{
|
||||||
|
@ -20,7 +52,32 @@ const DEFAULT_TABS = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
class ActionPreview extends Component {
|
interface Props<S, A extends Action<unknown>> {
|
||||||
|
styling: StylingFunction;
|
||||||
|
delta: Delta | null | undefined | false;
|
||||||
|
error: string | undefined;
|
||||||
|
nextState: S;
|
||||||
|
onInspectPath: (path: (string | number)[]) => void;
|
||||||
|
inspectedPath: (string | number)[];
|
||||||
|
tabName: string;
|
||||||
|
isWideLayout: boolean;
|
||||||
|
onSelectTab: (tabName: string) => void;
|
||||||
|
action: A;
|
||||||
|
actions: { [actionId: number]: PerformAction<A> };
|
||||||
|
selectedActionId: number | null;
|
||||||
|
startActionId: number | null;
|
||||||
|
computedStates: { state: S; error?: string }[];
|
||||||
|
base16Theme: Base16Theme;
|
||||||
|
invertTheme: boolean;
|
||||||
|
tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]);
|
||||||
|
dataTypeKey: string | undefined;
|
||||||
|
monitorState: MonitorState;
|
||||||
|
updateMonitorState: (monitorState: Partial<MonitorState>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActionPreview<S, A extends Action<unknown>> extends Component<
|
||||||
|
Props<S, A>
|
||||||
|
> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
tabName: DEFAULT_STATE.tabName
|
tabName: DEFAULT_STATE.tabName
|
||||||
};
|
};
|
||||||
|
@ -49,21 +106,21 @@ class ActionPreview extends Component {
|
||||||
updateMonitorState
|
updateMonitorState
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const renderedTabs =
|
const renderedTabs: Tab<S, A>[] =
|
||||||
typeof tabs === 'function'
|
typeof tabs === 'function'
|
||||||
? tabs(DEFAULT_TABS)
|
? tabs(DEFAULT_TABS as Tab<S, A>[])
|
||||||
: tabs
|
: tabs
|
||||||
? tabs
|
? tabs
|
||||||
: DEFAULT_TABS;
|
: (DEFAULT_TABS as Tab<S, A>[]);
|
||||||
|
|
||||||
const { component: TabComponent } =
|
const { component: TabComponent } =
|
||||||
renderedTabs.find(tab => tab.name === tabName) ||
|
renderedTabs.find(tab => tab.name === tabName)! ||
|
||||||
renderedTabs.find(tab => tab.name === DEFAULT_STATE.tabName);
|
renderedTabs.find(tab => tab.name === DEFAULT_STATE.tabName)!;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key="actionPreview" {...styling('actionPreview')}>
|
<div key="actionPreview" {...styling('actionPreview')}>
|
||||||
<ActionPreviewHeader
|
<ActionPreviewHeader
|
||||||
tabs={renderedTabs}
|
tabs={(renderedTabs as unknown) as Tab<unknown, Action<unknown>>[]}
|
||||||
{...{ styling, inspectedPath, onInspectPath, tabName, onSelectTab }}
|
{...{ styling, inspectedPath, onInspectPath, tabName, onSelectTab }}
|
||||||
/>
|
/>
|
||||||
{!error && (
|
{!error && (
|
||||||
|
@ -94,7 +151,11 @@ class ActionPreview extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
labelRenderer = ([key, ...rest], nodeType, expanded) => {
|
labelRenderer = (
|
||||||
|
[key, ...rest]: (string | number)[],
|
||||||
|
nodeType: string,
|
||||||
|
expanded: boolean
|
||||||
|
) => {
|
||||||
const { styling, onInspectPath, inspectedPath } = this.props;
|
const { styling, onInspectPath, inspectedPath } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
|
@ -1,16 +1,32 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { StylingFunction } from 'react-base16-styling';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { Tab } from './ActionPreview';
|
||||||
|
|
||||||
const ActionPreviewHeader = ({
|
interface Props<S, A extends Action<unknown>> {
|
||||||
|
styling: StylingFunction;
|
||||||
|
inspectedPath: (string | number)[];
|
||||||
|
onInspectPath: (path: (string | number)[]) => void;
|
||||||
|
tabName: string;
|
||||||
|
onSelectTab: (tabName: string) => void;
|
||||||
|
tabs: Tab<S, A>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionPreviewHeader: React.FunctionComponent<Props<
|
||||||
|
unknown,
|
||||||
|
Action<unknown>
|
||||||
|
>> = <S, A extends Action<unknown>>({
|
||||||
styling,
|
styling,
|
||||||
inspectedPath,
|
inspectedPath,
|
||||||
onInspectPath,
|
onInspectPath,
|
||||||
tabName,
|
tabName,
|
||||||
onSelectTab,
|
onSelectTab,
|
||||||
tabs
|
tabs
|
||||||
}) => (
|
}: Props<S, A>) => (
|
||||||
<div key="previewHeader" {...styling('previewHeader')}>
|
<div key="previewHeader" {...styling('previewHeader')}>
|
||||||
<div {...styling('tabSelector')}>
|
<div {...styling('tabSelector')}>
|
||||||
{tabs.map(tab => (
|
{tabs.map((tab: Tab<S, A>) => (
|
||||||
<div
|
<div
|
||||||
onClick={() => onSelectTab(tab.name)}
|
onClick={() => onSelectTab(tab.name)}
|
||||||
key={tab.name}
|
key={tab.name}
|
||||||
|
@ -39,7 +55,7 @@ const ActionPreviewHeader = ({
|
||||||
) : (
|
) : (
|
||||||
tabName
|
tabName
|
||||||
)}
|
)}
|
||||||
{inspectedPath.map((key, idx) =>
|
{inspectedPath.map((key: string | number, idx: number) =>
|
||||||
idx === inspectedPath.length - 1 ? (
|
idx === inspectedPath.length - 1 ? (
|
||||||
<span key={key}>{key}</span>
|
<span key={key}>{key}</span>
|
||||||
) : (
|
) : (
|
||||||
|
@ -57,4 +73,13 @@ const ActionPreviewHeader = ({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ActionPreviewHeader.propTypes = {
|
||||||
|
styling: PropTypes.func.isRequired,
|
||||||
|
inspectedPath: PropTypes.array.isRequired,
|
||||||
|
onInspectPath: PropTypes.func.isRequired,
|
||||||
|
tabName: PropTypes.string.isRequired,
|
||||||
|
onSelectTab: PropTypes.func.isRequired,
|
||||||
|
tabs: PropTypes.array.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
export default ActionPreviewHeader;
|
export default ActionPreviewHeader;
|
|
@ -1,17 +1,29 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { PropTypes } from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
createStylingFromTheme,
|
createStylingFromTheme,
|
||||||
base16Themes
|
base16Themes
|
||||||
} from './utils/createStylingFromTheme';
|
} from './utils/createStylingFromTheme';
|
||||||
import shouldPureComponentUpdate from 'react-pure-render/function';
|
import shouldPureComponentUpdate from 'react-pure-render/function';
|
||||||
import ActionList from './ActionList';
|
import ActionList from './ActionList';
|
||||||
import ActionPreview from './ActionPreview';
|
import ActionPreview, { Tab } from './ActionPreview';
|
||||||
import getInspectedState from './utils/getInspectedState';
|
import getInspectedState from './utils/getInspectedState';
|
||||||
import createDiffPatcher from './createDiffPatcher';
|
import createDiffPatcher from './createDiffPatcher';
|
||||||
import { getBase16Theme } from 'react-base16-styling';
|
import {
|
||||||
import { reducer, updateMonitorState } from './redux';
|
Base16Theme,
|
||||||
import { ActionCreators } from 'redux-devtools';
|
getBase16Theme,
|
||||||
|
StylingFunction,
|
||||||
|
Theme
|
||||||
|
} from 'react-base16-styling';
|
||||||
|
import {
|
||||||
|
MonitorAction,
|
||||||
|
MonitorState,
|
||||||
|
reducer,
|
||||||
|
updateMonitorState
|
||||||
|
} from './redux';
|
||||||
|
import { ActionCreators, LiftedAction, LiftedState } from 'redux-devtools';
|
||||||
|
import { Action, Dispatch } from 'redux';
|
||||||
|
import { Delta, DiffContext } from 'jsondiffpatch';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
commit,
|
commit,
|
||||||
|
@ -22,21 +34,43 @@ const {
|
||||||
reorderAction
|
reorderAction
|
||||||
} = ActionCreators;
|
} = ActionCreators;
|
||||||
|
|
||||||
function getLastActionId(props) {
|
export interface Props<S, A extends Action<unknown>>
|
||||||
|
extends LiftedState<S, A, MonitorState> {
|
||||||
|
dispatch: Dispatch<
|
||||||
|
MonitorAction | LiftedAction<S, A, MonitorState, MonitorAction>
|
||||||
|
>;
|
||||||
|
|
||||||
|
select: (state: S) => unknown;
|
||||||
|
supportImmutable: boolean;
|
||||||
|
draggableActions: boolean;
|
||||||
|
tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]);
|
||||||
|
theme: Theme;
|
||||||
|
invertTheme: boolean;
|
||||||
|
diffObjectHash?: (item: any, index: number) => string;
|
||||||
|
diffPropertyFilter?: (name: string, context: DiffContext) => boolean;
|
||||||
|
dataTypeKey?: string;
|
||||||
|
hideMainButtons?: boolean;
|
||||||
|
hideActionButtons?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLastActionId<S, A extends Action<unknown>>(props: Props<S, A>) {
|
||||||
return props.stagedActionIds[props.stagedActionIds.length - 1];
|
return props.stagedActionIds[props.stagedActionIds.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentActionId(props, monitorState) {
|
function getCurrentActionId<S, A extends Action<unknown>>(
|
||||||
|
props: Props<S, A>,
|
||||||
|
monitorState: MonitorState
|
||||||
|
) {
|
||||||
return monitorState.selectedActionId === null
|
return monitorState.selectedActionId === null
|
||||||
? props.stagedActionIds[props.currentStateIndex]
|
? props.stagedActionIds[props.currentStateIndex]
|
||||||
: monitorState.selectedActionId;
|
: monitorState.selectedActionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFromState(
|
function getFromState<S>(
|
||||||
actionIndex,
|
actionIndex: number,
|
||||||
stagedActionIds,
|
stagedActionIds: number[],
|
||||||
computedStates,
|
computedStates: { state: S; error?: string }[],
|
||||||
monitorState
|
monitorState: MonitorState
|
||||||
) {
|
) {
|
||||||
const { startActionId } = monitorState;
|
const { startActionId } = monitorState;
|
||||||
if (startActionId === null) {
|
if (startActionId === null) {
|
||||||
|
@ -47,7 +81,10 @@ function getFromState(
|
||||||
return computedStates[fromStateIdx];
|
return computedStates[fromStateIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
function createIntermediateState(props, monitorState) {
|
function createIntermediateState<S, A extends Action<unknown>>(
|
||||||
|
props: Props<S, A>,
|
||||||
|
monitorState: MonitorState
|
||||||
|
) {
|
||||||
const {
|
const {
|
||||||
supportImmutable,
|
supportImmutable,
|
||||||
computedStates,
|
computedStates,
|
||||||
|
@ -97,15 +134,27 @@ function createIntermediateState(props, monitorState) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createThemeState(props) {
|
function createThemeState<S, A extends Action<unknown>>(props: Props<S, A>) {
|
||||||
const base16Theme = getBase16Theme(props.theme, base16Themes);
|
const base16Theme = getBase16Theme(props.theme, base16Themes)!;
|
||||||
const styling = createStylingFromTheme(props.theme, props.invertTheme);
|
const styling = createStylingFromTheme(props.theme, props.invertTheme);
|
||||||
|
|
||||||
return { base16Theme, styling };
|
return { base16Theme, styling };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DevtoolsInspector extends Component {
|
interface State<S, A extends Action<unknown>> {
|
||||||
constructor(props) {
|
isWideLayout: boolean;
|
||||||
|
themeState: { base16Theme: Base16Theme; styling: StylingFunction };
|
||||||
|
delta: Delta | null | undefined | false;
|
||||||
|
nextState: S;
|
||||||
|
action: A;
|
||||||
|
error: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DevtoolsInspector<
|
||||||
|
S,
|
||||||
|
A extends Action<unknown>
|
||||||
|
> extends Component<Props<S, A>, State<S, A>> {
|
||||||
|
constructor(props: Props<S, A>) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
...createIntermediateState(props, props.monitorState),
|
...createIntermediateState(props, props.monitorState),
|
||||||
|
@ -142,7 +191,7 @@ export default class DevtoolsInspector extends Component {
|
||||||
static update = reducer;
|
static update = reducer;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
select: state => state,
|
select: (state: unknown) => state,
|
||||||
supportImmutable: false,
|
supportImmutable: false,
|
||||||
draggableActions: true,
|
draggableActions: true,
|
||||||
theme: 'inspector',
|
theme: 'inspector',
|
||||||
|
@ -151,29 +200,35 @@ export default class DevtoolsInspector extends Component {
|
||||||
|
|
||||||
shouldComponentUpdate = shouldPureComponentUpdate;
|
shouldComponentUpdate = shouldPureComponentUpdate;
|
||||||
|
|
||||||
|
updateSizeTimeout?: number;
|
||||||
|
inspectorRef?: HTMLDivElement | null;
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.updateSizeMode();
|
this.updateSizeMode();
|
||||||
this.updateSizeTimeout = setInterval(this.updateSizeMode.bind(this), 150);
|
this.updateSizeTimeout = window.setInterval(
|
||||||
|
this.updateSizeMode.bind(this),
|
||||||
|
150
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
clearTimeout(this.updateSizeTimeout);
|
clearTimeout(this.updateSizeTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMonitorState = monitorState => {
|
updateMonitorState = (monitorState: Partial<MonitorState>) => {
|
||||||
this.props.dispatch(updateMonitorState(monitorState));
|
this.props.dispatch(updateMonitorState(monitorState));
|
||||||
};
|
};
|
||||||
|
|
||||||
updateSizeMode() {
|
updateSizeMode() {
|
||||||
const isWideLayout = this.inspectorRef.offsetWidth > 500;
|
const isWideLayout = this.inspectorRef!.offsetWidth > 500;
|
||||||
|
|
||||||
if (isWideLayout !== this.state.isWideLayout) {
|
if (isWideLayout !== this.state.isWideLayout) {
|
||||||
this.setState({ isWideLayout });
|
this.setState({ isWideLayout });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps: Props<S, A>) {
|
||||||
let nextMonitorState = nextProps.monitorState;
|
const nextMonitorState = nextProps.monitorState;
|
||||||
const monitorState = this.props.monitorState;
|
const monitorState = this.props.monitorState;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -197,7 +252,7 @@ export default class DevtoolsInspector extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inspectorCreateRef = node => {
|
inspectorCreateRef: React.RefCallback<HTMLDivElement> = node => {
|
||||||
this.inspectorRef = node;
|
this.inspectorRef = node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -287,7 +342,9 @@ export default class DevtoolsInspector extends Component {
|
||||||
monitorState={this.props.monitorState}
|
monitorState={this.props.monitorState}
|
||||||
updateMonitorState={this.updateMonitorState}
|
updateMonitorState={this.updateMonitorState}
|
||||||
styling={styling}
|
styling={styling}
|
||||||
onInspectPath={this.handleInspectPath.bind(this, inspectedPathType)}
|
onInspectPath={keyPath =>
|
||||||
|
this.handleInspectPath(inspectedPathType, keyPath)
|
||||||
|
}
|
||||||
inspectedPath={monitorState[inspectedPathType]}
|
inspectedPath={monitorState[inspectedPathType]}
|
||||||
onSelectTab={this.handleSelectTab}
|
onSelectTab={this.handleSelectTab}
|
||||||
/>
|
/>
|
||||||
|
@ -295,11 +352,11 @@ export default class DevtoolsInspector extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleToggleAction = actionId => {
|
handleToggleAction = (actionId: number) => {
|
||||||
this.props.dispatch(toggleAction(actionId));
|
this.props.dispatch(toggleAction(actionId));
|
||||||
};
|
};
|
||||||
|
|
||||||
handleJumpToState = actionId => {
|
handleJumpToState = (actionId: number) => {
|
||||||
if (jumpToAction) {
|
if (jumpToAction) {
|
||||||
this.props.dispatch(jumpToAction(actionId));
|
this.props.dispatch(jumpToAction(actionId));
|
||||||
} else {
|
} else {
|
||||||
|
@ -309,7 +366,7 @@ export default class DevtoolsInspector extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleReorderAction = (actionId, beforeActionId) => {
|
handleReorderAction = (actionId: number, beforeActionId: number) => {
|
||||||
if (reorderAction)
|
if (reorderAction)
|
||||||
this.props.dispatch(reorderAction(actionId, beforeActionId));
|
this.props.dispatch(reorderAction(actionId, beforeActionId));
|
||||||
};
|
};
|
||||||
|
@ -322,11 +379,14 @@ export default class DevtoolsInspector extends Component {
|
||||||
this.props.dispatch(sweep());
|
this.props.dispatch(sweep());
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSearch = val => {
|
handleSearch = (val: string) => {
|
||||||
this.updateMonitorState({ searchValue: val });
|
this.updateMonitorState({ searchValue: val });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelectAction = (e, actionId) => {
|
handleSelectAction = (
|
||||||
|
e: React.MouseEvent<HTMLDivElement>,
|
||||||
|
actionId: number
|
||||||
|
) => {
|
||||||
const { monitorState } = this.props;
|
const { monitorState } = this.props;
|
||||||
let startActionId;
|
let startActionId;
|
||||||
let selectedActionId;
|
let selectedActionId;
|
||||||
|
@ -365,11 +425,14 @@ export default class DevtoolsInspector extends Component {
|
||||||
this.updateMonitorState({ startActionId, selectedActionId });
|
this.updateMonitorState({ startActionId, selectedActionId });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInspectPath = (pathType, path) => {
|
handleInspectPath = (
|
||||||
|
pathType: 'inspectedActionPath' | 'inspectedStatePath',
|
||||||
|
path: (string | number)[]
|
||||||
|
) => {
|
||||||
this.updateMonitorState({ [pathType]: path });
|
this.updateMonitorState({ [pathType]: path });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelectTab = tabName => {
|
handleSelectTab = (tabName: string) => {
|
||||||
this.updateMonitorState({ tabName });
|
this.updateMonitorState({ tabName });
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { PropTypes } from 'prop-types';
|
|
||||||
|
|
||||||
const RightSlider = ({ styling, shown, children, rotate }) => (
|
|
||||||
<div
|
|
||||||
{...styling([
|
|
||||||
'rightSlider',
|
|
||||||
shown && 'rightSliderShown',
|
|
||||||
rotate && 'rightSliderRotate',
|
|
||||||
rotate && shown && 'rightSliderRotateShown'
|
|
||||||
])}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
RightSlider.propTypes = {
|
|
||||||
shown: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RightSlider;
|
|
37
packages/redux-devtools-inspector/src/RightSlider.tsx
Normal file
37
packages/redux-devtools-inspector/src/RightSlider.tsx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { StylingFunction } from 'react-base16-styling';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
styling: StylingFunction;
|
||||||
|
shown?: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
rotate?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RightSlider: React.FunctionComponent<Props> = ({
|
||||||
|
styling,
|
||||||
|
shown,
|
||||||
|
children,
|
||||||
|
rotate
|
||||||
|
}) => (
|
||||||
|
<div
|
||||||
|
{...styling([
|
||||||
|
'rightSlider',
|
||||||
|
shown && 'rightSliderShown',
|
||||||
|
rotate && 'rightSliderRotate',
|
||||||
|
rotate && shown && 'rightSliderRotateShown'
|
||||||
|
])}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
RightSlider.propTypes = {
|
||||||
|
styling: PropTypes.func.isRequired,
|
||||||
|
shown: PropTypes.bool,
|
||||||
|
children: PropTypes.any.isRequired,
|
||||||
|
rotate: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RightSlider;
|
61
packages/redux-devtools-inspector/src/base16.ts
Normal file
61
packages/redux-devtools-inspector/src/base16.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
declare module 'base16' {
|
||||||
|
export interface Base16Theme {
|
||||||
|
scheme?: string;
|
||||||
|
author?: string;
|
||||||
|
base00: string;
|
||||||
|
base01: string;
|
||||||
|
base02: string;
|
||||||
|
base03: string;
|
||||||
|
base04: string;
|
||||||
|
base05: string;
|
||||||
|
base06: string;
|
||||||
|
base07: string;
|
||||||
|
base08: string;
|
||||||
|
base09: string;
|
||||||
|
base0A: string;
|
||||||
|
base0B: string;
|
||||||
|
base0C: string;
|
||||||
|
base0D: string;
|
||||||
|
base0E: string;
|
||||||
|
base0F: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const threezerotwofour: Base16Theme;
|
||||||
|
export const apathy: Base16Theme;
|
||||||
|
export const ashes: Base16Theme;
|
||||||
|
export const atelierDune: Base16Theme;
|
||||||
|
export const atelierForest: Base16Theme;
|
||||||
|
export const atelierHeath: Base16Theme;
|
||||||
|
export const atelierLakeside: Base16Theme;
|
||||||
|
export const atelierSeaside: Base16Theme;
|
||||||
|
export const bespin: Base16Theme;
|
||||||
|
export const brewer: Base16Theme;
|
||||||
|
export const bright: Base16Theme;
|
||||||
|
export const chalk: Base16Theme;
|
||||||
|
export const codeschool: Base16Theme;
|
||||||
|
export const colors: Base16Theme;
|
||||||
|
const _default: Base16Theme;
|
||||||
|
export default _default;
|
||||||
|
export const eighties: Base16Theme;
|
||||||
|
export const embers: Base16Theme;
|
||||||
|
export const flat: Base16Theme;
|
||||||
|
export const google: Base16Theme;
|
||||||
|
export const grayscale: Base16Theme;
|
||||||
|
export const greenscreen: Base16Theme;
|
||||||
|
export const harmonic: Base16Theme;
|
||||||
|
export const hopscotch: Base16Theme;
|
||||||
|
export const isotope: Base16Theme;
|
||||||
|
export const marrakesh: Base16Theme;
|
||||||
|
export const mocha: Base16Theme;
|
||||||
|
export const monokai: Base16Theme;
|
||||||
|
export const ocean: Base16Theme;
|
||||||
|
export const paraiso: Base16Theme;
|
||||||
|
export const pop: Base16Theme;
|
||||||
|
export const railscasts: Base16Theme;
|
||||||
|
export const shapeshifter: Base16Theme;
|
||||||
|
export const solarized: Base16Theme;
|
||||||
|
export const summerfruit: Base16Theme;
|
||||||
|
export const tomorrow: Base16Theme;
|
||||||
|
export const tube: Base16Theme;
|
||||||
|
export const twilight: Base16Theme;
|
||||||
|
}
|
|
@ -1,28 +1,37 @@
|
||||||
import { DiffPatcher } from 'jsondiffpatch/src/diffpatcher';
|
import { DiffContext, DiffPatcher } from 'jsondiffpatch';
|
||||||
|
|
||||||
const defaultObjectHash = (o, idx) =>
|
const defaultObjectHash = (o: any, idx: number) =>
|
||||||
(o === null && '$$null') ||
|
(o === null && '$$null') ||
|
||||||
(o && (o.id || o.id === 0) && `$$id:${JSON.stringify(o.id)}`) ||
|
(o && (o.id || o.id === 0) && `$$id:${JSON.stringify(o.id)}`) ||
|
||||||
(o && (o._id || o._id === 0) && `$$_id:${JSON.stringify(o._id)}`) ||
|
(o && (o._id || o._id === 0) && `$$_id:${JSON.stringify(o._id)}`) ||
|
||||||
'$$index:' + idx;
|
'$$index:' + idx;
|
||||||
|
|
||||||
const defaultPropertyFilter = (name, context) =>
|
const defaultPropertyFilter = (name: string, context: DiffContext) =>
|
||||||
typeof context.left[name] !== 'function' &&
|
typeof context.left[name] !== 'function' &&
|
||||||
typeof context.right[name] !== 'function';
|
typeof context.right[name] !== 'function';
|
||||||
|
|
||||||
const defaultDiffPatcher = new DiffPatcher({
|
const defaultDiffPatcher = new DiffPatcher({
|
||||||
arrays: { detectMove: false },
|
arrays: { detectMove: false } as {
|
||||||
|
detectMove: boolean;
|
||||||
|
includeValueOnMove: boolean;
|
||||||
|
},
|
||||||
objectHash: defaultObjectHash,
|
objectHash: defaultObjectHash,
|
||||||
propertyFilter: defaultPropertyFilter
|
propertyFilter: defaultPropertyFilter
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function createDiffPatcher(objectHash, propertyFilter) {
|
export default function createDiffPatcher(
|
||||||
|
objectHash: ((item: any, index: number) => string) | undefined,
|
||||||
|
propertyFilter: ((name: string, context: DiffContext) => boolean) | undefined
|
||||||
|
) {
|
||||||
if (!objectHash && !propertyFilter) {
|
if (!objectHash && !propertyFilter) {
|
||||||
return defaultDiffPatcher;
|
return defaultDiffPatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DiffPatcher({
|
return new DiffPatcher({
|
||||||
arrays: { detectMove: false },
|
arrays: { detectMove: false } as {
|
||||||
|
detectMove: boolean;
|
||||||
|
includeValueOnMove: boolean;
|
||||||
|
},
|
||||||
objectHash: objectHash || defaultObjectHash,
|
objectHash: objectHash || defaultObjectHash,
|
||||||
propertyFilter: propertyFilter || defaultPropertyFilter
|
propertyFilter: propertyFilter || defaultPropertyFilter
|
||||||
});
|
});
|
|
@ -1 +0,0 @@
|
||||||
export default from './DevtoolsInspector';
|
|
2
packages/redux-devtools-inspector/src/index.ts
Normal file
2
packages/redux-devtools-inspector/src/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import DevtoolsInspector from './DevtoolsInspector';
|
||||||
|
export default DevtoolsInspector;
|
6
packages/redux-devtools-inspector/src/jss-nested.ts
Normal file
6
packages/redux-devtools-inspector/src/jss-nested.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
declare module 'jss-nested' {
|
||||||
|
import { Plugin } from 'jss';
|
||||||
|
|
||||||
|
const jssNested: () => Plugin;
|
||||||
|
export default jssNested;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
declare module 'jss-vendor-prefixer' {
|
||||||
|
import { Plugin } from 'jss';
|
||||||
|
|
||||||
|
const jssVendorPrefixer: () => Plugin;
|
||||||
|
export default jssVendorPrefixer;
|
||||||
|
}
|
57
packages/redux-devtools-inspector/src/jss.ts
Normal file
57
packages/redux-devtools-inspector/src/jss.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
declare module 'jss' {
|
||||||
|
import * as css from 'csstype';
|
||||||
|
|
||||||
|
// TODO: Type data better, currently typed as any for allowing to override it
|
||||||
|
type FnValue<R> = R | ((data: any) => R);
|
||||||
|
|
||||||
|
type NormalCssProperties = css.Properties<string | number>;
|
||||||
|
type CssProperties = {
|
||||||
|
[K in keyof NormalCssProperties]: FnValue<NormalCssProperties[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Jss Style definitions
|
||||||
|
type JssStyleP<S> = CssProperties & {
|
||||||
|
[key: string]: FnValue<JssValue | S>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type JssStyle = JssStyleP<
|
||||||
|
JssStyleP<JssStyleP<JssStyleP<JssStyleP<JssStyleP<JssStyleP<void>>>>>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type Styles<Name extends string | number | symbol = string> = Record<
|
||||||
|
Name,
|
||||||
|
JssStyle | string
|
||||||
|
>;
|
||||||
|
export type Classes<Name extends string | number | symbol = string> = Record<
|
||||||
|
Name,
|
||||||
|
string
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type JssValue =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| Array<string | number | Array<string | number> | '!important'>
|
||||||
|
| null
|
||||||
|
| false;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface Plugin {}
|
||||||
|
|
||||||
|
export interface StyleSheet<
|
||||||
|
RuleName extends string | number | symbol = string | number | symbol
|
||||||
|
> {
|
||||||
|
classes: Classes<RuleName>;
|
||||||
|
attach(): this;
|
||||||
|
detach(): this;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Jss {
|
||||||
|
createStyleSheet<Name extends string | number | symbol>(
|
||||||
|
styles: Partial<Styles<Name>>
|
||||||
|
): StyleSheet<Name>;
|
||||||
|
use(...plugins: Plugin[]): this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jss: Jss;
|
||||||
|
export default jss;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
declare module 'react-base16-styling' {
|
||||||
|
export function getBase16Theme(
|
||||||
|
theme: Theme,
|
||||||
|
base16Themes?: Base16Theme[] | null
|
||||||
|
): Base16Theme | undefined | null;
|
||||||
|
}
|
8
packages/redux-devtools-inspector/src/react-dragula.ts
Normal file
8
packages/redux-devtools-inspector/src/react-dragula.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
declare module 'react-dragula' {
|
||||||
|
import { DragulaOptions, Drake } from 'dragula';
|
||||||
|
|
||||||
|
export default function(
|
||||||
|
containers: Array<HTMLElement>,
|
||||||
|
options: DragulaOptions
|
||||||
|
): Drake;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
declare module 'react-pure-render/function' {
|
||||||
|
export default function shouldPureComponentUpdate(
|
||||||
|
nextProps: unknown,
|
||||||
|
nextState: unknown
|
||||||
|
): boolean;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
declare module 'redux-devtools-themes' {
|
||||||
|
import { Base16Theme } from 'base16';
|
||||||
|
export * from 'base16';
|
||||||
|
export const nicinabox: Base16Theme;
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
const UPDATE_MONITOR_STATE = '@@redux-devtools-inspector/UPDATE_MONITOR_STATE';
|
|
||||||
|
|
||||||
export const DEFAULT_STATE = {
|
|
||||||
selectedActionId: null,
|
|
||||||
startActionId: null,
|
|
||||||
inspectedActionPath: [],
|
|
||||||
inspectedStatePath: [],
|
|
||||||
tabName: 'Diff'
|
|
||||||
};
|
|
||||||
|
|
||||||
export function updateMonitorState(monitorState) {
|
|
||||||
return { type: UPDATE_MONITOR_STATE, monitorState };
|
|
||||||
}
|
|
||||||
|
|
||||||
function reduceUpdateState(state, action) {
|
|
||||||
return action.type === UPDATE_MONITOR_STATE
|
|
||||||
? {
|
|
||||||
...state,
|
|
||||||
...action.monitorState
|
|
||||||
}
|
|
||||||
: state;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reducer(props, state = DEFAULT_STATE, action) {
|
|
||||||
return {
|
|
||||||
...reduceUpdateState(state, action)
|
|
||||||
};
|
|
||||||
}
|
|
52
packages/redux-devtools-inspector/src/redux.ts
Normal file
52
packages/redux-devtools-inspector/src/redux.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { Props } from './DevtoolsInspector';
|
||||||
|
|
||||||
|
const UPDATE_MONITOR_STATE = '@@redux-devtools-inspector/UPDATE_MONITOR_STATE';
|
||||||
|
interface UpdateMonitorStateAction {
|
||||||
|
type: typeof UPDATE_MONITOR_STATE;
|
||||||
|
monitorState: Partial<MonitorState>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MonitorAction = UpdateMonitorStateAction;
|
||||||
|
|
||||||
|
export interface MonitorState {
|
||||||
|
readonly selectedActionId: number | null;
|
||||||
|
readonly startActionId: number | null;
|
||||||
|
readonly inspectedActionPath: (string | number)[];
|
||||||
|
readonly inspectedStatePath: (string | number)[];
|
||||||
|
readonly tabName: string;
|
||||||
|
readonly searchValue?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_STATE: MonitorState = {
|
||||||
|
selectedActionId: null,
|
||||||
|
startActionId: null,
|
||||||
|
inspectedActionPath: [],
|
||||||
|
inspectedStatePath: [],
|
||||||
|
tabName: 'Diff'
|
||||||
|
};
|
||||||
|
|
||||||
|
export function updateMonitorState(
|
||||||
|
monitorState: Partial<MonitorState>
|
||||||
|
): UpdateMonitorStateAction {
|
||||||
|
return { type: UPDATE_MONITOR_STATE, monitorState };
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduceUpdateState(state: MonitorState, action: MonitorAction) {
|
||||||
|
return action.type === UPDATE_MONITOR_STATE
|
||||||
|
? {
|
||||||
|
...state,
|
||||||
|
...action.monitorState
|
||||||
|
}
|
||||||
|
: state;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reducer<S, A extends Action<unknown>>(
|
||||||
|
props: Props<S, A>,
|
||||||
|
state = DEFAULT_STATE,
|
||||||
|
action: MonitorAction
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...reduceUpdateState(state, action)
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,9 +1,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import JSONTree from 'react-json-tree';
|
import JSONTree from 'react-json-tree';
|
||||||
import getItemString from './getItemString';
|
import getItemString from './getItemString';
|
||||||
import getJsonTreeTheme from './getJsonTreeTheme';
|
import getJsonTreeTheme from './getJsonTreeTheme';
|
||||||
|
import { TabComponentProps } from '../ActionPreview';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
const ActionTab = ({
|
const ActionTab: React.FunctionComponent<TabComponentProps<
|
||||||
|
unknown,
|
||||||
|
Action<unknown>
|
||||||
|
>> = ({
|
||||||
action,
|
action,
|
||||||
styling,
|
styling,
|
||||||
base16Theme,
|
base16Theme,
|
||||||
|
@ -24,4 +30,14 @@ const ActionTab = ({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ActionTab.propTypes = {
|
||||||
|
action: PropTypes.any.isRequired,
|
||||||
|
styling: PropTypes.func.isRequired,
|
||||||
|
base16Theme: PropTypes.any.isRequired,
|
||||||
|
invertTheme: PropTypes.bool.isRequired,
|
||||||
|
labelRenderer: PropTypes.func.isRequired,
|
||||||
|
dataTypeKey: PropTypes.string,
|
||||||
|
isWideLayout: PropTypes.bool.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
export default ActionTab;
|
export default ActionTab;
|
|
@ -1,24 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import JSONDiff from './JSONDiff';
|
|
||||||
|
|
||||||
const DiffTab = ({
|
|
||||||
delta,
|
|
||||||
styling,
|
|
||||||
base16Theme,
|
|
||||||
invertTheme,
|
|
||||||
labelRenderer,
|
|
||||||
isWideLayout
|
|
||||||
}) => (
|
|
||||||
<JSONDiff
|
|
||||||
{...{
|
|
||||||
delta,
|
|
||||||
styling,
|
|
||||||
base16Theme,
|
|
||||||
invertTheme,
|
|
||||||
labelRenderer,
|
|
||||||
isWideLayout
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default DiffTab;
|
|
42
packages/redux-devtools-inspector/src/tabs/DiffTab.tsx
Normal file
42
packages/redux-devtools-inspector/src/tabs/DiffTab.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import JSONDiff from './JSONDiff';
|
||||||
|
import { TabComponentProps } from '../ActionPreview';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
const DiffTab: React.FunctionComponent<TabComponentProps<
|
||||||
|
unknown,
|
||||||
|
Action<unknown>
|
||||||
|
>> = ({
|
||||||
|
delta,
|
||||||
|
styling,
|
||||||
|
base16Theme,
|
||||||
|
invertTheme,
|
||||||
|
labelRenderer,
|
||||||
|
isWideLayout,
|
||||||
|
dataTypeKey
|
||||||
|
}) => (
|
||||||
|
<JSONDiff
|
||||||
|
{...{
|
||||||
|
delta,
|
||||||
|
styling,
|
||||||
|
base16Theme,
|
||||||
|
invertTheme,
|
||||||
|
labelRenderer,
|
||||||
|
isWideLayout,
|
||||||
|
dataTypeKey
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
DiffTab.propTypes = {
|
||||||
|
delta: PropTypes.any,
|
||||||
|
styling: PropTypes.func.isRequired,
|
||||||
|
base16Theme: PropTypes.any.isRequired,
|
||||||
|
invertTheme: PropTypes.bool.isRequired,
|
||||||
|
labelRenderer: PropTypes.func.isRequired,
|
||||||
|
isWideLayout: PropTypes.bool.isRequired,
|
||||||
|
dataTypeKey: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DiffTab;
|
|
@ -3,8 +3,10 @@ import JSONTree from 'react-json-tree';
|
||||||
import stringify from 'javascript-stringify';
|
import stringify from 'javascript-stringify';
|
||||||
import getItemString from './getItemString';
|
import getItemString from './getItemString';
|
||||||
import getJsonTreeTheme from './getJsonTreeTheme';
|
import getJsonTreeTheme from './getJsonTreeTheme';
|
||||||
|
import { Delta } from 'jsondiffpatch';
|
||||||
|
import { Base16Theme, StylingFunction } from 'react-base16-styling';
|
||||||
|
|
||||||
function stringifyAndShrink(val, isWideLayout) {
|
function stringifyAndShrink(val: any, isWideLayout?: boolean) {
|
||||||
if (val === null) {
|
if (val === null) {
|
||||||
return 'null';
|
return 'null';
|
||||||
}
|
}
|
||||||
|
@ -19,18 +21,22 @@ function stringifyAndShrink(val, isWideLayout) {
|
||||||
return str.length > 22 ? `${str.substr(0, 15)}…${str.substr(-5)}` : str;
|
return str.length > 22 ? `${str.substr(0, 15)}…${str.substr(-5)}` : str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expandFirstLevel = (keyName, data, level) => level <= 1;
|
const expandFirstLevel = (
|
||||||
|
keyName: (string | number)[],
|
||||||
|
data: any,
|
||||||
|
level: number
|
||||||
|
) => level <= 1;
|
||||||
|
|
||||||
function prepareDelta(value) {
|
function prepareDelta(value: any) {
|
||||||
if (value && value._t === 'a') {
|
if (value && value._t === 'a') {
|
||||||
const res = {};
|
const res: { [key: string]: any } = {};
|
||||||
for (let key in value) {
|
for (const key in value) {
|
||||||
if (key !== '_t') {
|
if (key !== '_t') {
|
||||||
if (key[0] === '_' && !value[key.substr(1)]) {
|
if (key.startsWith('_') && !value[key.substr(1)]) {
|
||||||
res[key.substr(1)] = value[key];
|
res[key.substr(1)] = value[key];
|
||||||
} else if (value['_' + key]) {
|
} else if (value['_' + key]) {
|
||||||
res[key] = [value['_' + key][0], value[key][0]];
|
res[key] = [value['_' + key][0], value[key][0]];
|
||||||
} else if (!value['_' + key] && key[0] !== '_') {
|
} else if (!value['_' + key] && !key.startsWith('_')) {
|
||||||
res[key] = value[key];
|
res[key] = value[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,14 +47,33 @@ function prepareDelta(value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class JSONDiff extends Component {
|
interface Props {
|
||||||
|
delta: Delta | null | undefined | false;
|
||||||
|
styling: StylingFunction;
|
||||||
|
base16Theme: Base16Theme;
|
||||||
|
invertTheme: boolean;
|
||||||
|
labelRenderer: (
|
||||||
|
keyPath: (string | number)[],
|
||||||
|
nodeType: string,
|
||||||
|
expanded: boolean,
|
||||||
|
expandable: boolean
|
||||||
|
) => React.ReactNode;
|
||||||
|
isWideLayout: boolean;
|
||||||
|
dataTypeKey: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class JSONDiff extends Component<Props, State> {
|
||||||
state = { data: {} };
|
state = { data: {} };
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.updateData();
|
this.updateData();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
if (prevProps.delta !== this.props.delta) {
|
if (prevProps.delta !== this.props.delta) {
|
||||||
this.updateData();
|
this.updateData();
|
||||||
}
|
}
|
||||||
|
@ -84,7 +109,7 @@ export default class JSONDiff extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemString = (type, data) =>
|
getItemString = (type: string, data: any) =>
|
||||||
getItemString(
|
getItemString(
|
||||||
this.props.styling,
|
this.props.styling,
|
||||||
type,
|
type,
|
||||||
|
@ -94,10 +119,10 @@ export default class JSONDiff extends Component {
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
valueRenderer = (raw, value) => {
|
valueRenderer = (raw: any, value: any) => {
|
||||||
const { styling, isWideLayout } = this.props;
|
const { styling, isWideLayout } = this.props;
|
||||||
|
|
||||||
function renderSpan(name, body) {
|
function renderSpan(name: string, body: string) {
|
||||||
return (
|
return (
|
||||||
<span key={name} {...styling(['diff', name])}>
|
<span key={name} {...styling(['diff', name])}>
|
||||||
{body}
|
{body}
|
|
@ -1,9 +1,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import JSONTree from 'react-json-tree';
|
import JSONTree from 'react-json-tree';
|
||||||
import getItemString from './getItemString';
|
import getItemString from './getItemString';
|
||||||
import getJsonTreeTheme from './getJsonTreeTheme';
|
import getJsonTreeTheme from './getJsonTreeTheme';
|
||||||
|
import { TabComponentProps } from '../ActionPreview';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
const StateTab = ({
|
const StateTab: React.FunctionComponent<TabComponentProps<
|
||||||
|
any,
|
||||||
|
Action<unknown>
|
||||||
|
>> = ({
|
||||||
nextState,
|
nextState,
|
||||||
styling,
|
styling,
|
||||||
base16Theme,
|
base16Theme,
|
||||||
|
@ -24,4 +30,14 @@ const StateTab = ({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
StateTab.propTypes = {
|
||||||
|
nextState: PropTypes.any.isRequired,
|
||||||
|
styling: PropTypes.func.isRequired,
|
||||||
|
base16Theme: PropTypes.any.isRequired,
|
||||||
|
invertTheme: PropTypes.bool.isRequired,
|
||||||
|
labelRenderer: PropTypes.func.isRequired,
|
||||||
|
dataTypeKey: PropTypes.string,
|
||||||
|
isWideLayout: PropTypes.bool.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
export default StateTab;
|
export default StateTab;
|
|
@ -1,10 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Iterable } from 'immutable';
|
import { Iterable } from 'immutable';
|
||||||
import isIterable from '../utils/isIterable';
|
import isIterable from '../utils/isIterable';
|
||||||
|
import { StylingFunction } from 'react-base16-styling';
|
||||||
|
|
||||||
const IS_IMMUTABLE_KEY = '@@__IS_IMMUTABLE__@@';
|
const IS_IMMUTABLE_KEY = '@@__IS_IMMUTABLE__@@';
|
||||||
|
|
||||||
function isImmutable(value) {
|
function isImmutable(value: any) {
|
||||||
return (
|
return (
|
||||||
Iterable.isKeyed(value) ||
|
Iterable.isKeyed(value) ||
|
||||||
Iterable.isIndexed(value) ||
|
Iterable.isIndexed(value) ||
|
||||||
|
@ -12,7 +13,7 @@ function isImmutable(value) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getShortTypeString(val, diff) {
|
function getShortTypeString(val: any, diff: boolean | undefined) {
|
||||||
if (diff && Array.isArray(val)) {
|
if (diff && Array.isArray(val)) {
|
||||||
val = val[val.length === 2 ? 1 : 0];
|
val = val[val.length === 2 ? 1 : 0];
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,12 @@ function getShortTypeString(val, diff) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getText(type, data, isWideLayout, isDiff) {
|
function getText(
|
||||||
|
type: string,
|
||||||
|
data: any,
|
||||||
|
isWideLayout: boolean,
|
||||||
|
isDiff: boolean | undefined
|
||||||
|
) {
|
||||||
if (type === 'Object') {
|
if (type === 'Object') {
|
||||||
const keys = Object.keys(data);
|
const keys = Object.keys(data);
|
||||||
if (!isWideLayout) return keys.length ? '{…}' : '{}';
|
if (!isWideLayout) return keys.length ? '{…}' : '{}';
|
||||||
|
@ -55,7 +61,7 @@ function getText(type, data, isWideLayout, isDiff) {
|
||||||
|
|
||||||
const str = data
|
const str = data
|
||||||
.slice(0, 4)
|
.slice(0, 4)
|
||||||
.map(val => getShortTypeString(val, isDiff))
|
.map((val: any) => getShortTypeString(val, isDiff))
|
||||||
.concat(data.length > 4 ? ['…'] : [])
|
.concat(data.length > 4 ? ['…'] : [])
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
|
@ -66,12 +72,12 @@ function getText(type, data, isWideLayout, isDiff) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getItemString = (
|
const getItemString = (
|
||||||
styling,
|
styling: StylingFunction,
|
||||||
type,
|
type: string,
|
||||||
data,
|
data: any,
|
||||||
dataTypeKey,
|
dataTypeKey: string | undefined,
|
||||||
isWideLayout,
|
isWideLayout: boolean,
|
||||||
isDiff
|
isDiff?: boolean
|
||||||
) => (
|
) => (
|
||||||
<span {...styling('treeItemHint')}>
|
<span {...styling('treeItemHint')}>
|
||||||
{data[IS_IMMUTABLE_KEY] ? 'Immutable' : ''}
|
{data[IS_IMMUTABLE_KEY] ? 'Immutable' : ''}
|
|
@ -1,6 +1,10 @@
|
||||||
export default function getJsonTreeTheme(base16Theme) {
|
import { Base16Theme, StylingConfig, StylingValue } from 'react-base16-styling';
|
||||||
|
|
||||||
|
export default function getJsonTreeTheme(
|
||||||
|
base16Theme: Base16Theme
|
||||||
|
): StylingConfig {
|
||||||
return {
|
return {
|
||||||
extend: base16Theme,
|
extend: base16Theme as StylingValue,
|
||||||
nestedNode: ({ style }, keyPath, nodeType, expanded) => ({
|
nestedNode: ({ style }, keyPath, nodeType, expanded) => ({
|
||||||
style: {
|
style: {
|
||||||
...style,
|
...style,
|
|
@ -1,7 +1,7 @@
|
||||||
import jss from 'jss';
|
import jss, { Styles, StyleSheet } from 'jss';
|
||||||
import jssVendorPrefixer from 'jss-vendor-prefixer';
|
import jssVendorPrefixer from 'jss-vendor-prefixer';
|
||||||
import jssNested from 'jss-nested';
|
import jssNested from 'jss-nested';
|
||||||
import { createStyling } from 'react-base16-styling';
|
import { Base16Theme, createStyling } from 'react-base16-styling';
|
||||||
import rgba from 'hex-rgba';
|
import rgba from 'hex-rgba';
|
||||||
import inspector from '../themes/inspector';
|
import inspector from '../themes/inspector';
|
||||||
import * as reduxThemes from 'redux-devtools-themes';
|
import * as reduxThemes from 'redux-devtools-themes';
|
||||||
|
@ -10,7 +10,7 @@ import * as inspectorThemes from '../themes';
|
||||||
jss.use(jssVendorPrefixer());
|
jss.use(jssVendorPrefixer());
|
||||||
jss.use(jssNested());
|
jss.use(jssNested());
|
||||||
|
|
||||||
const colorMap = theme => ({
|
const colorMap = (theme: Base16Theme) => ({
|
||||||
TEXT_COLOR: theme.base06,
|
TEXT_COLOR: theme.base06,
|
||||||
TEXT_PLACEHOLDER_COLOR: rgba(theme.base06, 60),
|
TEXT_PLACEHOLDER_COLOR: rgba(theme.base06, 60),
|
||||||
BACKGROUND_COLOR: theme.base00,
|
BACKGROUND_COLOR: theme.base00,
|
||||||
|
@ -36,7 +36,12 @@ const colorMap = theme => ({
|
||||||
ERROR_COLOR: theme.base08
|
ERROR_COLOR: theme.base08
|
||||||
});
|
});
|
||||||
|
|
||||||
const getSheetFromColorMap = map => ({
|
type Color = keyof ReturnType<typeof colorMap>;
|
||||||
|
type ColorMap = {
|
||||||
|
[color in Color]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSheetFromColorMap = (map: ColorMap): Partial<Styles> => ({
|
||||||
inspector: {
|
inspector: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
'flex-direction': 'column',
|
'flex-direction': 'column',
|
||||||
|
@ -386,9 +391,9 @@ const getSheetFromColorMap = map => ({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let themeSheet;
|
let themeSheet: StyleSheet;
|
||||||
|
|
||||||
const getDefaultThemeStyling = theme => {
|
const getDefaultThemeStyling = (theme: Base16Theme) => {
|
||||||
if (themeSheet) {
|
if (themeSheet) {
|
||||||
themeSheet.detach();
|
themeSheet.detach();
|
||||||
}
|
}
|
||||||
|
@ -400,7 +405,10 @@ const getDefaultThemeStyling = theme => {
|
||||||
return themeSheet.classes;
|
return themeSheet.classes;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const base16Themes = { ...reduxThemes, ...inspectorThemes };
|
export const base16Themes = ({
|
||||||
|
...reduxThemes,
|
||||||
|
...inspectorThemes
|
||||||
|
} as unknown) as Base16Theme[];
|
||||||
|
|
||||||
export const createStylingFromTheme = createStyling(getDefaultThemeStyling, {
|
export const createStylingFromTheme = createStyling(getDefaultThemeStyling, {
|
||||||
defaultBase16: inspector,
|
defaultBase16: inspector,
|
|
@ -1,30 +0,0 @@
|
||||||
function deepMapCached(obj, f, ctx, cache) {
|
|
||||||
cache.push(obj);
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
return obj.map(function(val, key) {
|
|
||||||
val = f.call(ctx, val, key);
|
|
||||||
return typeof val === 'object' && cache.indexOf(val) === -1
|
|
||||||
? deepMapCached(val, f, ctx, cache)
|
|
||||||
: val;
|
|
||||||
});
|
|
||||||
} else if (typeof obj === 'object') {
|
|
||||||
const res = {};
|
|
||||||
for (const key in obj) {
|
|
||||||
let val = obj[key];
|
|
||||||
if (val && typeof val === 'object') {
|
|
||||||
val = f.call(ctx, val, key);
|
|
||||||
res[key] =
|
|
||||||
cache.indexOf(val) === -1 ? deepMapCached(val, f, ctx, cache) : val;
|
|
||||||
} else {
|
|
||||||
res[key] = f.call(ctx, val, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function deepMap(obj, f, ctx) {
|
|
||||||
return deepMapCached(obj, f, ctx, []);
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Iterable, fromJS } from 'immutable';
|
import { Iterable, fromJS } from 'immutable';
|
||||||
import isIterable from './isIterable';
|
import isIterable from './isIterable';
|
||||||
|
|
||||||
function iterateToKey(obj, key) {
|
function iterateToKey(obj: any, key: string | number) {
|
||||||
// maybe there's a better way, dunno
|
// maybe there's a better way, dunno
|
||||||
let idx = 0;
|
let idx = 0;
|
||||||
for (let entry of obj) {
|
for (const entry of obj) {
|
||||||
if (Array.isArray(entry)) {
|
if (Array.isArray(entry)) {
|
||||||
if (entry[0] === key) return entry[1];
|
if (entry[0] === key) return entry[1];
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,11 +15,15 @@ function iterateToKey(obj, key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function getInspectedState(state, path, convertImmutable) {
|
export default function getInspectedState<S>(
|
||||||
|
state: S,
|
||||||
|
path: (string | number)[],
|
||||||
|
convertImmutable: boolean
|
||||||
|
): S {
|
||||||
state =
|
state =
|
||||||
path && path.length
|
path && path.length
|
||||||
? {
|
? ({
|
||||||
[path[path.length - 1]]: path.reduce((s, key) => {
|
[path[path.length - 1]]: path.reduce((s: any, key) => {
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +36,7 @@ export default function getInspectedState(state, path, convertImmutable) {
|
||||||
|
|
||||||
return s[key];
|
return s[key];
|
||||||
}, state)
|
}, state)
|
||||||
}
|
} as S)
|
||||||
: state;
|
: state;
|
||||||
|
|
||||||
if (convertImmutable) {
|
if (convertImmutable) {
|
|
@ -1,4 +1,4 @@
|
||||||
export default function isIterable(obj) {
|
export default function isIterable(obj: any) {
|
||||||
return (
|
return (
|
||||||
obj !== null &&
|
obj !== null &&
|
||||||
typeof obj === 'object' &&
|
typeof obj === 'object' &&
|
7
packages/redux-devtools-inspector/tsconfig.json
Normal file
7
packages/redux-devtools-inspector/tsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.react.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user