diff --git a/package.json b/package.json index 026a3bf7..78acddc6 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,15 @@ "@typescript-eslint/parser": "^3.9.0", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", + "clean-webpack-plugin": "^3.0.0", + "cross-env": "^7.0.2", "eslint": "^7.6.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-jest": "^23.20.0", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-react": "^7.20.5", + "html-webpack-plugin": "^4.3.0", "jest": "^26.2.2", "lerna": "^3.22.1", "prettier": "^2.0.5", diff --git a/packages/react-base16-styling/src/index.ts b/packages/react-base16-styling/src/index.ts index 8f65c04d..f53667f2 100644 --- a/packages/react-base16-styling/src/index.ts +++ b/packages/react-base16-styling/src/index.ts @@ -142,7 +142,7 @@ const mergeStylings = ( const getStylingByKeys = ( mergedStyling: StylingConfig, - keys: string | string[], + keys: (string | false) | (string | false)[], ...args: any[] ): Styling => { if (keys === null) { @@ -153,7 +153,9 @@ const getStylingByKeys = ( keys = [keys]; } - const styles = keys.map((key) => mergedStyling[key]).filter(Boolean); + const styles = keys + .map((key) => mergedStyling[key as string]) + .filter(Boolean); const props = styles.reduce( (obj, s) => { diff --git a/packages/react-base16-styling/src/types.ts b/packages/react-base16-styling/src/types.ts index 7226b163..2896d1f6 100644 --- a/packages/react-base16-styling/src/types.ts +++ b/packages/react-base16-styling/src/types.ts @@ -27,6 +27,6 @@ export type StylingConfig = { export type Theme = string | Base16Theme | StylingConfig; export type StylingFunction = ( - keys: string | string[], + keys: (string | false) | (string | false)[], ...rest: any[] ) => Styling; diff --git a/packages/react-dock/demo/src/App.tsx b/packages/react-dock/demo/src/App.tsx index 68e76708..e5a06ed1 100644 --- a/packages/react-dock/demo/src/App.tsx +++ b/packages/react-dock/demo/src/App.tsx @@ -1,5 +1,5 @@ -import React, { Component } from 'react'; import { hot } from 'react-hot-loader/root'; +import React, { Component } from 'react'; import Button from 'react-bootstrap/Button'; import Form from 'react-bootstrap/Form'; import { BsX } from 'react-icons/bs'; diff --git a/packages/react-dock/tsconfig.webpack.json b/packages/react-dock/tsconfig.webpack.json index 86bc1286..d030bb04 100644 --- a/packages/react-dock/tsconfig.webpack.json +++ b/packages/react-dock/tsconfig.webpack.json @@ -1,4 +1,7 @@ { "extends": "../../tsconfig.base.json", + "compilerOptions": { + "js" + }, "include": ["webpack.config.ts"] } diff --git a/packages/redux-devtools-inspector/.babelrc b/packages/redux-devtools-inspector/.babelrc index 530ac9c9..0d42ef44 100644 --- a/packages/redux-devtools-inspector/.babelrc +++ b/packages/redux-devtools-inspector/.babelrc @@ -1,9 +1,8 @@ { - "presets": ["@babel/preset-env", "@babel/preset-react"], - "plugins": [ - "@babel/plugin-transform-runtime", - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-export-default-from", - "@babel/plugin-proposal-do-expressions" - ] + "presets": [ + "@babel/preset-env", + "@babel/preset-react", + "@babel/preset-typescript" + ], + "plugins": ["@babel/plugin-proposal-class-properties"] } diff --git a/packages/redux-devtools-inspector/.eslintignore b/packages/redux-devtools-inspector/.eslintignore new file mode 100644 index 00000000..a65b4177 --- /dev/null +++ b/packages/redux-devtools-inspector/.eslintignore @@ -0,0 +1 @@ +lib diff --git a/packages/redux-devtools-inspector/.eslintrc.js b/packages/redux-devtools-inspector/.eslintrc.js new file mode 100644 index 00000000..8cc0f569 --- /dev/null +++ b/packages/redux-devtools-inspector/.eslintrc.js @@ -0,0 +1,29 @@ +module.exports = { + extends: '../../.eslintrc', + overrides: [ + { + files: ['*.ts', '*.tsx'], + extends: '../../eslintrc.ts.react.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + }, + { + files: ['demo/**/*.ts', 'demo/**/*.tsx'], + extends: '../../eslintrc.ts.react.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./demo/tsconfig.json'], + }, + }, + { + files: ['webpack.config.ts'], + extends: '../../eslintrc.ts.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.webpack.json'], + }, + }, + ], +}; diff --git a/packages/redux-devtools-inspector/.npmignore b/packages/redux-devtools-inspector/.npmignore deleted file mode 100644 index 7e7ce1e8..00000000 --- a/packages/redux-devtools-inspector/.npmignore +++ /dev/null @@ -1,8 +0,0 @@ -static -src -demo -.* -webpack.config.js -index.html -*.gif -*.png diff --git a/packages/redux-devtools-inspector/demo/src/js/index.js b/packages/redux-devtools-inspector/demo/src/js/index.js index bf54b977..76306010 100644 --- a/packages/redux-devtools-inspector/demo/src/js/index.js +++ b/packages/redux-devtools-inspector/demo/src/js/index.js @@ -1,4 +1,3 @@ -import '@babel/polyfill'; import React from 'react'; import { render } from 'react-dom'; import DemoApp from './DemoApp'; diff --git a/packages/redux-devtools-inspector/demo/tsconfig.json b/packages/redux-devtools-inspector/demo/tsconfig.json new file mode 100644 index 00000000..2d04eaee --- /dev/null +++ b/packages/redux-devtools-inspector/demo/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../tsconfig.react.base.json", + "include": ["../src", "src"] +} diff --git a/packages/redux-devtools-inspector/jest.config.js b/packages/redux-devtools-inspector/jest.config.js new file mode 100644 index 00000000..8824c114 --- /dev/null +++ b/packages/redux-devtools-inspector/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + preset: 'ts-jest', +}; diff --git a/packages/redux-devtools-inspector/package.json b/packages/redux-devtools-inspector/package.json index 5797942d..ba8b19d9 100644 --- a/packages/redux-devtools-inspector/package.json +++ b/packages/redux-devtools-inspector/package.json @@ -2,70 +2,42 @@ "name": "redux-devtools-inspector", "version": "0.13.1", "description": "Redux DevTools Diff Monitor", - "scripts": { - "build": "npm run build:lib", - "build:lib": "cross-env NODE_ENV=production babel src --out-dir lib", - "build:demo": "cross-env NODE_ENV=production webpack -p", - "stats": "webpack --profile --json > stats.json", - "start": "webpack-dev-server", - "lint": "eslint --ext .jsx,.js --max-warnings 0 src", - "preversion": "npm run lint", - "version": "npm run build:demo && git add -A .", - "postversion": "git push", - "prepare": "npm run build:lib", - "prepublishOnly": "npm run build:lib", - "gh": "git subtree push --prefix demo/dist origin gh-pages" - }, - "main": "lib/index.js", - "repository": { - "url": "https://github.com/reduxjs/redux-devtools" - }, - "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-do-expressions": "^7.10.4", - "@babel/plugin-proposal-export-default-from": "^7.10.4", - "@babel/plugin-transform-runtime": "^7.11.0", - "@babel/polyfill": "^7.10.4", - "@babel/preset-env": "^7.11.0", - "@babel/preset-react": "^7.10.4", - "babel-loader": "^8.1.0", - "base16": "^1.0.0", - "clean-webpack-plugin": "^3.0.0", - "connected-react-router": "^6.8.0", - "cross-env": "^7.0.2", - "history": "^4.10.1", - "html-webpack-plugin": "^4.3.0", - "immutable": "^4.0.0-rc.12", - "lodash.isequalwith": "^4.4.0", - "lodash.shuffle": "^4.2.0", - "react": "^16.13.1", - "react-bootstrap": "^1.3.0", - "react-dom": "^16.13.1", - "react-redux": "^7.2.1", - "react-router": "^5.2.0", - "react-transform-hmr": "^1.0.4", - "redux": "^4.0.5", - "redux-devtools": "^3.6.1", - "redux-devtools-dock-monitor": "^1.1.4", - "redux-logger": "^3.0.6", - "seamless-immutable": "^7.1.4", - "webpack": "^4.44.1", - "webpack-cli": "^3.3.12", - "webpack-dev-server": "^3.11.0" - }, - "peerDependencies": { - "react": "^16.3.0", - "react-dom": "^16.3.0" + "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-inspector", + "bugs": { + "url": "https://github.com/reduxjs/redux-devtools/issues" }, + "license": "MIT", "author": "Alexander (http://kuzya.org/)", "contributors": [ "Mihail Diordiev (https://github.com/zalmoxisus)" ], - "license": "MIT", + "files": [ + "lib", + "src" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "url": "https://github.com/reduxjs/redux-devtools" + }, + "scripts": { + "start": "webpack-dev-server", + "stats": "webpack --profile --json > stats.json", + "build:demo": "NODE_ENV=production webpack -p", + "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", + "clean": "rimraf lib", + "lint": "eslint . --ext .ts,.tsx", + "lint:fix": "eslint . --ext .ts,.tsx --fix", + "type-check": "tsc --noEmit", + "type-check:watch": "npm run type-check -- --watch", + "preversion": "npm run type-check && npm run lint", + "prepublishOnly": "npm run clean && npm run build" + }, "dependencies": { - "babel-runtime": "^6.26.0", + "@types/dragula": "^3.7.0", + "@types/prop-types": "^15.7.3", "dateformat": "^3.0.3", "hex-rgba": "^1.0.2", "javascript-stringify": "^2.0.1", @@ -77,7 +49,33 @@ "react-base16-styling": "^0.7.0", "react-dragula": "^1.1.17", "react-json-tree": "^0.12.1", - "react-pure-render": "^1.0.2", "redux-devtools-themes": "^1.0.0" + }, + "devDependencies": { + "@types/dateformat": "^3.0.1", + "@types/lodash.debounce": "^4.0.6", + "@types/react": "^16.9.46", + "@types/react-dragula": "^1.1.0", + "base16": "^1.0.0", + "connected-react-router": "^6.8.0", + "history": "^4.10.1", + "immutable": "^4.0.0-rc.12", + "lodash.isequalwith": "^4.4.0", + "lodash.shuffle": "^4.2.0", + "react": "^16.13.1", + "react-bootstrap": "^1.3.0", + "react-dom": "^16.13.1", + "react-redux": "^7.2.1", + "react-router": "^5.2.0", + "redux": "^4.0.5", + "redux-devtools": "^3.6.1", + "redux-devtools-dock-monitor": "^1.1.4", + "redux-logger": "^3.0.6", + "seamless-immutable": "^7.1.4" + }, + "peerDependencies": { + "@types/react": "^16.3.18", + "react": "^16.3.0", + "redux": "^3.4.0 || ^4.0.0" } } diff --git a/packages/redux-devtools-inspector/src/.noderequirer.json b/packages/redux-devtools-inspector/src/.noderequirer.json deleted file mode 100644 index 20e76308..00000000 --- a/packages/redux-devtools-inspector/src/.noderequirer.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "import": true -} diff --git a/packages/redux-devtools-inspector/src/ActionList.jsx b/packages/redux-devtools-inspector/src/ActionList.tsx similarity index 67% rename from packages/redux-devtools-inspector/src/ActionList.jsx rename to packages/redux-devtools-inspector/src/ActionList.tsx index a93e7aeb..05080dfb 100644 --- a/packages/redux-devtools-inspector/src/ActionList.jsx +++ b/packages/redux-devtools-inspector/src/ActionList.tsx @@ -1,10 +1,17 @@ -import React, { Component } from 'react'; +import React, { PureComponent, RefCallback } from 'react'; +import { Drake } from 'dragula'; import dragula from 'react-dragula'; +import { Action } from 'redux'; +import { PerformAction } from 'redux-devtools'; +import { StylingFunction } from 'react-base16-styling'; import ActionListRow from './ActionListRow'; import ActionListHeader from './ActionListHeader'; -import shouldPureComponentUpdate from 'react-pure-render/function'; -function getTimestamps(actions, actionIds, actionId) { +function getTimestamps>( + actions: { [actionId: number]: PerformAction }, + actionIds: number[], + actionId: number +) { const idx = actionIds.indexOf(actionId); const prevActionId = actionIds[idx - 1]; @@ -14,10 +21,37 @@ function getTimestamps(actions, actionIds, actionId) { }; } -export default class ActionList extends Component { - shouldComponentUpdate = shouldPureComponentUpdate; +interface Props> { + actions: { [actionId: number]: PerformAction }; + 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; + onSearch: (value: string) => void; + onSelect: (e: React.MouseEvent, actionId: number) => void; + onToggleAction: (actionId: number) => void; + onJumpToState: (actionId: number) => void; + onCommit: () => void; + onSweep: () => void; + onReorderAction: (actionId: number, beforeActionId: number) => void; + currentActionId: number; + lastActionId: number; +} - UNSAFE_componentWillReceiveProps(nextProps) { +export default class ActionList< + A extends Action +> extends PureComponent> { + node?: HTMLDivElement | null; + scrollDown?: boolean; + drake?: Drake; + + UNSAFE_componentWillReceiveProps(nextProps: Props) { const node = this.node; if (!node) { this.scrollDown = true; @@ -35,22 +69,22 @@ export default class ActionList extends Component { this.scrollToBottom(); if (!this.props.draggableActions) return; - const container = this.node; + const container = this.node!; this.drake = dragula([container], { copy: false, copySortSource: false, mirrorContainer: container, accepts: (el, target, source, sibling) => - !sibling || parseInt(sibling.getAttribute('data-id')), + !sibling || !!parseInt(sibling.getAttribute('data-id')!), moves: (el, source, handle) => - parseInt(el.getAttribute('data-id')) && - handle.className.indexOf('selectorButton') !== 0, + !!parseInt(el!.getAttribute('data-id')!) && + handle!.className.indexOf('selectorButton') !== 0, }).on('drop', (el, target, source, sibling) => { let beforeActionId = this.props.actionIds.length; if (sibling && sibling.className.indexOf('gu-mirror') === -1) { - 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); }); } @@ -69,7 +103,7 @@ export default class ActionList extends Component { } } - getRef = (node) => { + getRef: RefCallback = (node) => { this.node = node; }; @@ -97,8 +131,9 @@ export default class ActionList extends Component { const filteredActionIds = searchValue ? actionIds.filter( (id) => - actions[id].action.type.toLowerCase().indexOf(lowerSearchValue) !== - -1 + (actions[id].action.type as string) + .toLowerCase() + .indexOf(lowerSearchValue as string) !== -1 ) : actionIds; diff --git a/packages/redux-devtools-inspector/src/ActionListHeader.jsx b/packages/redux-devtools-inspector/src/ActionListHeader.tsx similarity index 100% rename from packages/redux-devtools-inspector/src/ActionListHeader.jsx rename to packages/redux-devtools-inspector/src/ActionListHeader.tsx diff --git a/packages/redux-devtools-inspector/src/ActionListRow.jsx b/packages/redux-devtools-inspector/src/ActionListRow.tsx similarity index 69% rename from packages/redux-devtools-inspector/src/ActionListRow.jsx rename to packages/redux-devtools-inspector/src/ActionListRow.tsx index a9caf7df..16c17d1d 100644 --- a/packages/redux-devtools-inspector/src/ActionListRow.jsx +++ b/packages/redux-devtools-inspector/src/ActionListRow.tsx @@ -1,15 +1,40 @@ -import React, { Component } from 'react'; -import { PropTypes } from 'prop-types'; -import shouldPureComponentUpdate from 'react-pure-render/function'; +import React, { MouseEvent, MouseEventHandler, PureComponent } from 'react'; +import PropTypes from 'prop-types'; import dateformat from 'dateformat'; import debounce from 'lodash.debounce'; +import { StylingFunction } from 'react-base16-styling'; +import { Action } from 'redux'; import RightSlider from './RightSlider'; const BUTTON_SKIP = 'Skip'; const BUTTON_JUMP = 'Jump'; -export default class ActionListRow extends Component { - state = { hover: false }; +type Button = typeof BUTTON_SKIP | typeof BUTTON_JUMP; + +interface Props> { + styling: StylingFunction; + actionId: number; + isInitAction: boolean; + isSelected: boolean; + isInFuture: boolean; + onSelect: MouseEventHandler; + timestamps: { current: number; previous: number }; + action: A; + onToggleClick: () => void; + onJumpClick: () => void; + onCommitClick: () => void; + hideActionButtons: boolean | undefined; + isSkipped: boolean; +} + +interface State { + hover: boolean; +} + +export default class ActionListRow< + A extends Action +> extends PureComponent, State> { + state: State = { hover: false }; static propTypes = { styling: PropTypes.func.isRequired, @@ -25,8 +50,6 @@ export default class ActionListRow extends Component { isSkipped: PropTypes.bool.isRequired, }; - shouldComponentUpdate = shouldPureComponentUpdate; - render() { const { styling, @@ -44,18 +67,26 @@ export default class ActionListRow extends Component { const timeDelta = timestamps.current - timestamps.previous; const showButtons = (hover && !isInitAction) || isSkipped; - const isButtonSelected = (btn) => btn === BUTTON_SKIP && isSkipped; + const isButtonSelected = (btn: Button) => btn === BUTTON_SKIP && isSkipped; let actionType = action.type; if (typeof actionType === 'undefined') actionType = ''; else if (actionType === null) actionType = ''; - else actionType = actionType.toString() || ''; + else actionType = (actionType as string).toString() || ''; return (
+ } + onMouseLeave={ + (!hideActionButtons && this.handleMouseLeave) as MouseEventHandler< + HTMLDivElement + > + } onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseEnter} data-id={actionId} @@ -76,7 +107,7 @@ export default class ActionListRow extends Component { isSkipped && 'actionListItemNameSkipped', ])} > - {actionType} + {actionType as string}
{hideActionButtons ? ( @@ -103,12 +134,12 @@ export default class ActionListRow extends Component {
- {[BUTTON_JUMP, BUTTON_SKIP].map( + {([BUTTON_JUMP, BUTTON_SKIP] as const).map( (btn) => (!isInitAction || btn !== BUTTON_SKIP) && (
this.handleButtonClick(btn, e)} {...styling( [ 'selectorButton', @@ -131,7 +162,7 @@ export default class ActionListRow extends Component { ); } - handleButtonClick(btn, e) { + handleButtonClick(btn: Button, e: MouseEvent) { e.stopPropagation(); switch (btn) { @@ -144,8 +175,8 @@ export default class ActionListRow extends Component { } } - handleMouseEnter = (e) => { - if (this.hover) return; + handleMouseEnter = (e: MouseEvent) => { + if (this.state.hover) return; this.handleMouseLeave.cancel(); this.handleMouseEnterDebounced(e.buttons); }; @@ -160,8 +191,13 @@ export default class ActionListRow extends Component { if (this.state.hover) this.setState({ hover: false }); }, 100); - handleMouseDown = (e) => { - if (e.target.className.indexOf('selectorButton') === 0) return; + handleMouseDown = (e: MouseEvent) => { + if ( + ((e.target as unknown) as { className: string[] }).className.indexOf( + 'selectorButton' + ) === 0 + ) + return; this.handleMouseLeave(); }; } diff --git a/packages/redux-devtools-inspector/src/ActionPreview.jsx b/packages/redux-devtools-inspector/src/ActionPreview.tsx similarity index 100% rename from packages/redux-devtools-inspector/src/ActionPreview.jsx rename to packages/redux-devtools-inspector/src/ActionPreview.tsx diff --git a/packages/redux-devtools-inspector/src/ActionPreviewHeader.jsx b/packages/redux-devtools-inspector/src/ActionPreviewHeader.tsx similarity index 100% rename from packages/redux-devtools-inspector/src/ActionPreviewHeader.jsx rename to packages/redux-devtools-inspector/src/ActionPreviewHeader.tsx diff --git a/packages/redux-devtools-inspector/src/DevtoolsInspector.js b/packages/redux-devtools-inspector/src/DevtoolsInspector.tsx similarity index 71% rename from packages/redux-devtools-inspector/src/DevtoolsInspector.js rename to packages/redux-devtools-inspector/src/DevtoolsInspector.tsx index 4ef9193d..8fa95302 100644 --- a/packages/redux-devtools-inspector/src/DevtoolsInspector.js +++ b/packages/redux-devtools-inspector/src/DevtoolsInspector.tsx @@ -1,18 +1,30 @@ -import React, { Component } from 'react'; -import { PropTypes } from 'prop-types'; +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Base16Theme } from 'redux-devtools-themes'; +import { + getBase16Theme, + invertTheme, + StylingFunction, +} from 'react-base16-styling'; +import { ActionCreators, LiftedAction, LiftedState } from 'redux-devtools'; +import { Action, Dispatch } from 'redux'; +import { Delta, DiffContext } from 'jsondiffpatch'; import { createStylingFromTheme, base16Themes, } from './utils/createStylingFromTheme'; -import shouldPureComponentUpdate from 'react-pure-render/function'; import ActionList from './ActionList'; import ActionPreview from './ActionPreview'; import getInspectedState from './utils/getInspectedState'; import createDiffPatcher from './createDiffPatcher'; -import { getBase16Theme, invertTheme } from 'react-base16-styling'; -import { reducer, updateMonitorState } from './redux'; -import { ActionCreators } from 'redux-devtools'; +import { + DevtoolsInspectorAction, + DevtoolsInspectorState, + reducer, + updateMonitorState, +} from './redux'; +// eslint-disable-next-line @typescript-eslint/unbound-method const { commit, sweep, @@ -22,21 +34,26 @@ const { reorderAction, } = ActionCreators; -function getLastActionId(props) { +function getLastActionId>( + props: DevtoolsInspectorProps +) { return props.stagedActionIds[props.stagedActionIds.length - 1]; } -function getCurrentActionId(props, monitorState) { +function getCurrentActionId>( + props: DevtoolsInspectorProps, + monitorState: DevtoolsInspectorState +) { return monitorState.selectedActionId === null ? props.stagedActionIds[props.currentStateIndex] : monitorState.selectedActionId; } -function getFromState( - actionIndex, - stagedActionIds, - computedStates, - monitorState +function getFromState( + actionIndex: number, + stagedActionIds: number[], + computedStates: { state: S; error?: string }[], + monitorState: DevtoolsInspectorState ) { const { startActionId } = monitorState; if (startActionId === null) { @@ -47,7 +64,10 @@ function getFromState( return computedStates[fromStateIdx]; } -function createIntermediateState(props, monitorState) { +function createIntermediateState>( + props: DevtoolsInspectorProps, + monitorState: DevtoolsInspectorState +) { const { supportImmutable, computedStates, @@ -97,8 +117,10 @@ function createIntermediateState(props, monitorState) { }; } -function createThemeState(props) { - const base16Theme = getBase16Theme(props.theme, base16Themes); +function createThemeState>( + props: DevtoolsInspectorProps +) { + const base16Theme = getBase16Theme(props.theme, base16Themes)!; const theme = props.invertTheme ? invertTheme(props.theme) : props.theme; const styling = createStylingFromTheme(theme); @@ -106,15 +128,43 @@ function createThemeState(props) { return { base16Theme, styling }; } -export default class DevtoolsInspector extends Component { - constructor(props) { - super(props); - this.state = { - ...createIntermediateState(props, props.monitorState), - isWideLayout: false, - themeState: createThemeState(props), - }; - } +export interface DevtoolsInspectorProps> + extends LiftedState { + dispatch: Dispatch< + DevtoolsInspectorAction | LiftedAction + >; + preserveScrollTop?: boolean; + draggableActions: boolean; + select: (state: S) => unknown; + theme: keyof typeof base16Themes | Base16Theme; + supportImmutable: boolean; + diffObjectHash?: (item: unknown, index: number) => string; + diffPropertyFilter?: (name: string, context: DiffContext) => boolean; + hideMainButtons?: boolean; + hideActionButtons?: boolean; + invertTheme: boolean; + dataTypeKey?: string; + tabs: unknown; +} + +interface State> { + delta: Delta | null | undefined | false; + nextState: S; + action: A; + error: string | undefined; + isWideLayout: boolean; + themeState: { base16Theme: Base16Theme; styling: StylingFunction }; +} + +export default class DevtoolsInspector< + S, + A extends Action +> extends PureComponent, State> { + state: State = { + ...createIntermediateState(this.props, this.props.monitorState), + isWideLayout: false, + themeState: createThemeState(this.props), + }; static propTypes = { dispatch: PropTypes.func, @@ -127,7 +177,6 @@ export default class DevtoolsInspector extends Component { }), preserveScrollTop: PropTypes.bool, draggableActions: PropTypes.bool, - stagedActions: PropTypes.array, select: PropTypes.func.isRequired, theme: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), supportImmutable: PropTypes.bool, @@ -144,38 +193,42 @@ export default class DevtoolsInspector extends Component { static update = reducer; static defaultProps = { - select: (state) => state, + select: (state: unknown) => state, supportImmutable: false, draggableActions: true, theme: 'inspector', invertTheme: true, }; - shouldComponentUpdate = shouldPureComponentUpdate; + updateSizeTimeout?: number; + inspectorRef?: HTMLDivElement | null; componentDidMount() { this.updateSizeMode(); - this.updateSizeTimeout = setInterval(this.updateSizeMode.bind(this), 150); + this.updateSizeTimeout = window.setInterval( + this.updateSizeMode.bind(this), + 150 + ); } componentWillUnmount() { clearTimeout(this.updateSizeTimeout); } - updateMonitorState = (monitorState) => { + updateMonitorState = (monitorState: Partial) => { this.props.dispatch(updateMonitorState(monitorState)); }; updateSizeMode() { - const isWideLayout = this.inspectorRef.offsetWidth > 500; + const isWideLayout = this.inspectorRef!.offsetWidth > 500; if (isWideLayout !== this.state.isWideLayout) { this.setState({ isWideLayout }); } } - UNSAFE_componentWillReceiveProps(nextProps) { - let nextMonitorState = nextProps.monitorState; + UNSAFE_componentWillReceiveProps(nextProps: DevtoolsInspectorProps) { + const nextMonitorState = nextProps.monitorState; const monitorState = this.props.monitorState; if ( @@ -199,7 +252,7 @@ export default class DevtoolsInspector extends Component { } } - inspectorCreateRef = (node) => { + inspectorCreateRef: React.RefCallback = (node) => { this.inspectorRef = node; }; @@ -297,11 +350,11 @@ export default class DevtoolsInspector extends Component { ); } - handleToggleAction = (actionId) => { + handleToggleAction = (actionId: number) => { this.props.dispatch(toggleAction(actionId)); }; - handleJumpToState = (actionId) => { + handleJumpToState = (actionId: number) => { if (jumpToAction) { this.props.dispatch(jumpToAction(actionId)); } else { @@ -311,7 +364,7 @@ export default class DevtoolsInspector extends Component { } }; - handleReorderAction = (actionId, beforeActionId) => { + handleReorderAction = (actionId: number, beforeActionId: number) => { if (reorderAction) this.props.dispatch(reorderAction(actionId, beforeActionId)); }; @@ -324,11 +377,14 @@ export default class DevtoolsInspector extends Component { this.props.dispatch(sweep()); }; - handleSearch = (val) => { + handleSearch = (val: string) => { this.updateMonitorState({ searchValue: val }); }; - handleSelectAction = (e, actionId) => { + handleSelectAction = ( + e: React.MouseEvent, + actionId: number + ) => { const { monitorState } = this.props; let startActionId; let selectedActionId; @@ -367,11 +423,14 @@ export default class DevtoolsInspector extends Component { this.updateMonitorState({ startActionId, selectedActionId }); }; - handleInspectPath = (pathType, path) => { + handleInspectPath = ( + pathType: 'inspectedActionPath' | 'inspectedStatePath', + path: (string | number)[] + ) => { this.updateMonitorState({ [pathType]: path }); }; - handleSelectTab = (tabName) => { + handleSelectTab = (tabName: string) => { this.updateMonitorState({ tabName }); }; } diff --git a/packages/redux-devtools-inspector/src/RightSlider.jsx b/packages/redux-devtools-inspector/src/RightSlider.tsx similarity index 90% rename from packages/redux-devtools-inspector/src/RightSlider.jsx rename to packages/redux-devtools-inspector/src/RightSlider.tsx index 15c93e48..0b65307f 100644 --- a/packages/redux-devtools-inspector/src/RightSlider.jsx +++ b/packages/redux-devtools-inspector/src/RightSlider.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { PropTypes } from 'prop-types'; +import PropTypes from 'prop-types'; const RightSlider = ({ styling, shown, children, rotate }) => (