diff --git a/packages/redux-devtools-instrument/src/instrument.ts b/packages/redux-devtools-instrument/src/instrument.ts index 0829bb8f..98fbacd4 100644 --- a/packages/redux-devtools-instrument/src/instrument.ts +++ b/packages/redux-devtools-instrument/src/instrument.ts @@ -42,7 +42,7 @@ const isChromeOrNode = process.release && process.release.name === 'node'); -interface PerformAction> { +export interface PerformAction> { type: typeof ActionTypes.PERFORM_ACTION; action: A; timestamp: number; diff --git a/packages/redux-devtools-log-monitor/.babelrc b/packages/redux-devtools-log-monitor/.babelrc index 2d8ea82b..0d42ef44 100644 --- a/packages/redux-devtools-log-monitor/.babelrc +++ b/packages/redux-devtools-log-monitor/.babelrc @@ -1,7 +1,8 @@ { - "presets": ["@babel/preset-env", "@babel/preset-react"], - "plugins": [ - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-export-default-from" - ] + "presets": [ + "@babel/preset-env", + "@babel/preset-react", + "@babel/preset-typescript" + ], + "plugins": ["@babel/plugin-proposal-class-properties"] } diff --git a/packages/redux-devtools-log-monitor/.eslintignore b/packages/redux-devtools-log-monitor/.eslintignore new file mode 100644 index 00000000..a65b4177 --- /dev/null +++ b/packages/redux-devtools-log-monitor/.eslintignore @@ -0,0 +1 @@ +lib diff --git a/packages/redux-devtools-log-monitor/.eslintrc.js b/packages/redux-devtools-log-monitor/.eslintrc.js new file mode 100644 index 00000000..e98ddf18 --- /dev/null +++ b/packages/redux-devtools-log-monitor/.eslintrc.js @@ -0,0 +1,13 @@ +module.exports = { + extends: '../../.eslintrc', + overrides: [ + { + files: ['*.ts', '*.tsx'], + extends: '../../eslintrc.ts.react.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + }, + ], +}; diff --git a/packages/redux-devtools-log-monitor/package.json b/packages/redux-devtools-log-monitor/package.json index 43bfb4b3..35342048 100644 --- a/packages/redux-devtools-log-monitor/package.json +++ b/packages/redux-devtools-log-monitor/package.json @@ -2,21 +2,6 @@ "name": "redux-devtools-log-monitor", "version": "2.0.1", "description": "The default tree view monitor for Redux DevTools", - "main": "lib/index.js", - "files": [ - "lib", - "src" - ], - "scripts": { - "clean": "rimraf lib", - "build": "babel src --out-dir lib", - "prepare": "npm run build", - "prepublishOnly": "npm run test && npm run clean && npm run build" - }, - "repository": { - "type": "git", - "url": "https://github.com/reduxjs/redux-devtools" - }, "keywords": [ "redux", "devtools", @@ -26,31 +11,51 @@ "time travel", "live edit" ], - "author": "Dan Abramov (http://github.com/gaearon)", - "license": "MIT", + "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-log-monitor", "bugs": { "url": "https://github.com/reduxjs/redux-devtools/issues" }, - "homepage": "https://github.com/reduxjs/redux-devtools", + "license": "MIT", + "author": "Dan Abramov (http://github.com/gaearon)", + "files": [ + "lib", + "src" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/reduxjs/redux-devtools" + }, + "scripts": { + "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 && npm run test", + "prepublishOnly": "npm run clean && npm run build" + }, + "dependencies": { + "@types/prop-types": "^15.7.3", + "@types/redux-devtools-themes": "^1.0.0", + "lodash.debounce": "^4.0.8", + "prop-types": "^15.7.2", + "react-json-tree": "^0.12.1", + "redux-devtools-themes": "^1.0.0" + }, "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-export-default-from": "^7.10.4", - "@babel/preset-env": "^7.11.0", - "@babel/preset-react": "^7.10.4", - "babel-loader": "^8.1.0", - "rimraf": "^3.0.2" + "@types/lodash.debounce": "^4.0.6", + "@types/react": "^16.9.46", + "react": "^16.13.1", + "redux": "^4.0.5", + "redux-devtools": "^3.6.1" }, "peerDependencies": { "react": "^16.3.0", "redux-devtools": "^3.4.0" - }, - "dependencies": { - "lodash.debounce": "^4.0.8", - "prop-types": "^15.7.2", - "react-json-tree": "^0.12.1", - "react-pure-render": "^1.0.2", - "redux-devtools-themes": "^1.0.0" } } diff --git a/packages/redux-devtools-log-monitor/src/LogMonitor.js b/packages/redux-devtools-log-monitor/src/LogMonitor.tsx similarity index 79% rename from packages/redux-devtools-log-monitor/src/LogMonitor.js rename to packages/redux-devtools-log-monitor/src/LogMonitor.tsx index 4c0867d7..09a6032e 100644 --- a/packages/redux-devtools-log-monitor/src/LogMonitor.js +++ b/packages/redux-devtools-log-monitor/src/LogMonitor.tsx @@ -1,17 +1,26 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import shouldPureComponentUpdate from 'react-pure-render/function'; +import { Action, Dispatch } from 'redux'; import * as themes from 'redux-devtools-themes'; -import { ActionCreators } from 'redux-devtools'; -import { updateScrollTop, startConsecutiveToggle } from './actions'; -import reducer from './reducers'; +import { ActionCreators, LiftedAction, LiftedState } from 'redux-devtools'; +import { Base16Theme } from 'base16'; +import { + updateScrollTop, + startConsecutiveToggle, + LogMonitorAction, +} from './actions'; +import reducer, { LogMonitorState } from './reducers'; import LogMonitorButtonBar from './LogMonitorButtonBar'; import LogMonitorEntryList from './LogMonitorEntryList'; import debounce from 'lodash.debounce'; +// eslint-disable-next-line @typescript-eslint/unbound-method const { toggleAction, setActionsActive } = ActionCreators; -const styles = { +const styles: { + container: React.CSSProperties; + elements: React.CSSProperties; +} = { container: { fontFamily: 'monaco, Consolas, Lucida Console, monospace', position: 'relative', @@ -32,7 +41,23 @@ const styles = { }, }; -export default class LogMonitor extends Component { +export interface LogMonitorProps> + extends LiftedState { + dispatch: Dispatch>; + + preserveScrollTop: boolean; + select: (state: S) => unknown; + theme: keyof typeof themes | Base16Theme; + expandActionRoot: boolean; + expandStateRoot: boolean; + markStateDiff: boolean; + hideMainButtons?: boolean; +} + +export default class LogMonitor< + S, + A extends Action +> extends PureComponent> { static update = reducer; static propTypes = { @@ -56,7 +81,7 @@ export default class LogMonitor extends Component { }; static defaultProps = { - select: (state) => state, + select: (state: unknown) => state, theme: 'nicinabox', preserveScrollTop: true, expandActionRoot: true, @@ -64,22 +89,14 @@ export default class LogMonitor extends Component { markStateDiff: false, }; - shouldComponentUpdate = shouldPureComponentUpdate; + scrollDown?: boolean; + node?: HTMLDivElement | null; updateScrollTop = debounce(() => { const node = this.node; this.props.dispatch(updateScrollTop(node ? node.scrollTop : 0)); }, 500); - constructor(props) { - super(props); - this.handleToggleAction = this.handleToggleAction.bind(this); - this.handleToggleConsecutiveAction = this.handleToggleConsecutiveAction.bind( - this - ); - this.getRef = this.getRef.bind(this); - } - scroll() { const node = this.node; if (!node) { @@ -114,7 +131,7 @@ export default class LogMonitor extends Component { } } - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: LogMonitorProps) { const node = this.node; if (!node) { this.scrollDown = true; @@ -134,11 +151,11 @@ export default class LogMonitor extends Component { this.scroll(); } - handleToggleAction(id) { + handleToggleAction = (id: number) => { this.props.dispatch(toggleAction(id)); - } + }; - handleToggleConsecutiveAction(id) { + handleToggleConsecutiveAction = (id: number) => { const { monitorState, actionsById } = this.props; const { consecutiveToggleStartId } = monitorState; if (consecutiveToggleStartId && actionsById[consecutiveToggleStartId]) { @@ -151,10 +168,10 @@ export default class LogMonitor extends Component { } else if (id > 0) { this.props.dispatch(startConsecutiveToggle(id)); } - } + }; getTheme() { - let { theme } = this.props; + const { theme } = this.props; if (typeof theme !== 'string') { return theme; } @@ -170,9 +187,9 @@ export default class LogMonitor extends Component { return themes.nicinabox; } - getRef(node) { + getRef: React.RefCallback = (node) => { this.node = node; - } + }; render() { const theme = this.getTheme(); diff --git a/packages/redux-devtools-log-monitor/src/LogMonitorButton.js b/packages/redux-devtools-log-monitor/src/LogMonitorButton.tsx similarity index 64% rename from packages/redux-devtools-log-monitor/src/LogMonitorButton.js rename to packages/redux-devtools-log-monitor/src/LogMonitorButton.tsx index 23e9587c..7f6e749e 100644 --- a/packages/redux-devtools-log-monitor/src/LogMonitorButton.js +++ b/packages/redux-devtools-log-monitor/src/LogMonitorButton.tsx @@ -1,8 +1,9 @@ -import React from 'react'; +import React, { CSSProperties } from 'react'; +import { Base16Theme } from 'base16'; +import { Action } from 'redux'; import brighten from './brighten'; -import shouldPureComponentUpdate from 'react-pure-render/function'; -const styles = { +const styles: { base: CSSProperties } = { base: { cursor: 'pointer', fontWeight: 'bold', @@ -20,48 +21,50 @@ const styles = { }, }; -export default class LogMonitorButton extends React.Component { - shouldComponentUpdate = shouldPureComponentUpdate; +interface State { + hovered: boolean; + active: boolean; +} - constructor(props) { - super(props); +interface Props { + theme: Base16Theme; + onClick: () => void; + enabled: boolean; +} - this.handleMouseEnter = this.handleMouseEnter.bind(this); - this.handleMouseLeave = this.handleMouseLeave.bind(this); - this.handleMouseDown = this.handleMouseDown.bind(this); - this.handleMouseUp = this.handleMouseUp.bind(this); - this.onClick = this.onClick.bind(this); +export default class LogMonitorButton extends React.PureComponent< + Props, + State +> { + state: State = { + hovered: false, + active: false, + }; - this.state = { - hovered: false, - active: false, - }; - } - - handleMouseEnter() { + handleMouseEnter = () => { this.setState({ hovered: true }); - } + }; - handleMouseLeave() { + handleMouseLeave = () => { this.setState({ hovered: false }); - } + }; - handleMouseDown() { + handleMouseDown = () => { this.setState({ active: true }); - } + }; - handleMouseUp() { + handleMouseUp = () => { this.setState({ active: false }); - } + }; - onClick() { + onClick = () => { if (!this.props.enabled) { return; } if (this.props.onClick) { this.props.onClick(); } - } + }; render() { let style = { diff --git a/packages/redux-devtools-log-monitor/src/LogMonitorButtonBar.js b/packages/redux-devtools-log-monitor/src/LogMonitorButtonBar.tsx similarity index 62% rename from packages/redux-devtools-log-monitor/src/LogMonitorButtonBar.js rename to packages/redux-devtools-log-monitor/src/LogMonitorButtonBar.tsx index 27b5b34f..c84e1189 100644 --- a/packages/redux-devtools-log-monitor/src/LogMonitorButtonBar.js +++ b/packages/redux-devtools-log-monitor/src/LogMonitorButtonBar.tsx @@ -1,12 +1,16 @@ -import React, { Component } from 'react'; +import React, { CSSProperties, PureComponent } from 'react'; import PropTypes from 'prop-types'; -import shouldPureComponentUpdate from 'react-pure-render/function'; -import { ActionCreators } from 'redux-devtools'; +import { ActionCreators, LiftedAction } from 'redux-devtools'; import LogMonitorButton from './LogMonitorButton'; +import { Action, Dispatch } from 'redux'; +import { Base16Theme } from 'base16'; +import { LogMonitorAction } from './actions'; +import { LogMonitorState } from './reducers'; +// eslint-disable-next-line @typescript-eslint/unbound-method const { reset, rollback, commit, sweep } = ActionCreators; -const style = { +const style: CSSProperties = { textAlign: 'center', borderBottomWidth: 1, borderBottomStyle: 'solid', @@ -16,37 +20,37 @@ const style = { flexDirection: 'row', }; -export default class LogMonitorButtonBar extends Component { +interface Props> { + theme: Base16Theme; + dispatch: Dispatch>; + hasStates: boolean; + hasSkippedActions: boolean; +} + +export default class LogMonitorButtonBar< + S, + A extends Action +> extends PureComponent> { static propTypes = { dispatch: PropTypes.func, theme: PropTypes.object, }; - shouldComponentUpdate = shouldPureComponentUpdate; - - constructor(props) { - super(props); - this.handleReset = this.handleReset.bind(this); - this.handleRollback = this.handleRollback.bind(this); - this.handleSweep = this.handleSweep.bind(this); - this.handleCommit = this.handleCommit.bind(this); - } - - handleRollback() { + handleRollback = () => { this.props.dispatch(rollback()); - } + }; - handleSweep() { + handleSweep = () => { this.props.dispatch(sweep()); - } + }; - handleCommit() { + handleCommit = () => { this.props.dispatch(commit()); - } + }; - handleReset() { + handleReset = () => { this.props.dispatch(reset()); - } + }; render() { const { theme, hasStates, hasSkippedActions } = this.props; diff --git a/packages/redux-devtools-log-monitor/src/LogMonitorEntry.js b/packages/redux-devtools-log-monitor/src/LogMonitorEntry.tsx similarity index 94% rename from packages/redux-devtools-log-monitor/src/LogMonitorEntry.js rename to packages/redux-devtools-log-monitor/src/LogMonitorEntry.tsx index 6f4aea78..1d36c8b9 100644 --- a/packages/redux-devtools-log-monitor/src/LogMonitorEntry.js +++ b/packages/redux-devtools-log-monitor/src/LogMonitorEntry.tsx @@ -1,8 +1,7 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import JSONTree from 'react-json-tree'; import LogMonitorEntryAction from './LogMonitorEntryAction'; -import shouldPureComponentUpdate from 'react-pure-render/function'; const styles = { entry: { @@ -23,7 +22,7 @@ const dataIsEqual = (data, previousData, keyPath) => { return getDeepItem(data, path) === getDeepItem(previousData, path); }; -export default class LogMonitorEntry extends Component { +export default class LogMonitorEntry extends PureComponent { static propTypes = { state: PropTypes.object.isRequired, action: PropTypes.object.isRequired, @@ -39,8 +38,6 @@ export default class LogMonitorEntry extends Component { expandStateRoot: PropTypes.bool, }; - shouldComponentUpdate = shouldPureComponentUpdate; - constructor(props) { super(props); this.handleActionClick = this.handleActionClick.bind(this); diff --git a/packages/redux-devtools-log-monitor/src/LogMonitorEntryAction.js b/packages/redux-devtools-log-monitor/src/LogMonitorEntryAction.tsx similarity index 100% rename from packages/redux-devtools-log-monitor/src/LogMonitorEntryAction.js rename to packages/redux-devtools-log-monitor/src/LogMonitorEntryAction.tsx diff --git a/packages/redux-devtools-log-monitor/src/LogMonitorEntryList.js b/packages/redux-devtools-log-monitor/src/LogMonitorEntryList.tsx similarity index 70% rename from packages/redux-devtools-log-monitor/src/LogMonitorEntryList.js rename to packages/redux-devtools-log-monitor/src/LogMonitorEntryList.tsx index 7acaa370..23a88447 100644 --- a/packages/redux-devtools-log-monitor/src/LogMonitorEntryList.js +++ b/packages/redux-devtools-log-monitor/src/LogMonitorEntryList.tsx @@ -1,9 +1,31 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; +import { Action } from 'redux'; +import { PerformAction } from 'redux-devtools'; +import { Base16Theme } from 'base16'; import LogMonitorEntry from './LogMonitorEntry'; -import shouldPureComponentUpdate from 'react-pure-render/function'; -export default class LogMonitorEntryList extends Component { +interface Props> { + actionsById: { [actionId: number]: PerformAction }; + computedStates: { state: S; error?: string }[]; + stagedActionIds: number[]; + skippedActionIds: number[]; + currentStateIndex: number; + consecutiveToggleStartId: number | null | undefined; + + select: (state: S) => unknown; + onActionClick: (id: number) => void; + theme: Base16Theme; + expandActionRoot: boolean; + expandStateRoot: boolean; + markStateDiff: boolean; + onActionShiftClick: (id: number) => void; +} + +export default class LogMonitorEntryList< + S, + A extends Action +> extends PureComponent> { static propTypes = { actionsById: PropTypes.object, computedStates: PropTypes.array, @@ -19,8 +41,6 @@ export default class LogMonitorEntryList extends Component { expandStateRoot: PropTypes.bool, }; - shouldComponentUpdate = shouldPureComponentUpdate; - render() { const elements = []; const { diff --git a/packages/redux-devtools-log-monitor/src/actions.js b/packages/redux-devtools-log-monitor/src/actions.ts similarity index 100% rename from packages/redux-devtools-log-monitor/src/actions.js rename to packages/redux-devtools-log-monitor/src/actions.ts diff --git a/packages/redux-devtools-log-monitor/src/brighten.js b/packages/redux-devtools-log-monitor/src/brighten.ts similarity index 100% rename from packages/redux-devtools-log-monitor/src/brighten.js rename to packages/redux-devtools-log-monitor/src/brighten.ts diff --git a/packages/redux-devtools-log-monitor/src/index.js b/packages/redux-devtools-log-monitor/src/index.js deleted file mode 100644 index 0a3a289b..00000000 --- a/packages/redux-devtools-log-monitor/src/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './LogMonitor'; diff --git a/packages/redux-devtools-log-monitor/src/index.ts b/packages/redux-devtools-log-monitor/src/index.ts new file mode 100644 index 00000000..174328d5 --- /dev/null +++ b/packages/redux-devtools-log-monitor/src/index.ts @@ -0,0 +1,2 @@ +import LogMonitor from './LogMonitor'; +export default LogMonitor; diff --git a/packages/redux-devtools-log-monitor/src/reducers.js b/packages/redux-devtools-log-monitor/src/reducers.js deleted file mode 100644 index 1312a9b9..00000000 --- a/packages/redux-devtools-log-monitor/src/reducers.js +++ /dev/null @@ -1,24 +0,0 @@ -import { UPDATE_SCROLL_TOP, START_CONSECUTIVE_TOGGLE } from './actions'; - -function initialScrollTop(props, state = 0, action) { - if (!props.preserveScrollTop) { - return 0; - } - - return action.type === UPDATE_SCROLL_TOP ? action.scrollTop : state; -} - -function startConsecutiveToggle(props, state, action) { - return action.type === START_CONSECUTIVE_TOGGLE ? action.id : state; -} - -export default function reducer(props, state = {}, action) { - return { - initialScrollTop: initialScrollTop(props, state.initialScrollTop, action), - consecutiveToggleStartId: startConsecutiveToggle( - props, - state.consecutiveToggleStartId, - action - ), - }; -} diff --git a/packages/redux-devtools-log-monitor/src/reducers.ts b/packages/redux-devtools-log-monitor/src/reducers.ts new file mode 100644 index 00000000..eea3387e --- /dev/null +++ b/packages/redux-devtools-log-monitor/src/reducers.ts @@ -0,0 +1,47 @@ +import { + UPDATE_SCROLL_TOP, + START_CONSECUTIVE_TOGGLE, + LogMonitorAction, +} from './actions'; +import { Action } from 'redux'; +import { LogMonitorProps } from './LogMonitor'; + +function initialScrollTop>( + props: LogMonitorProps, + state = 0, + action: LogMonitorAction +) { + if (!props.preserveScrollTop) { + return 0; + } + + return action.type === UPDATE_SCROLL_TOP ? action.scrollTop : state; +} + +function startConsecutiveToggle>( + props: LogMonitorProps, + state: number | null | undefined, + action: LogMonitorAction +) { + return action.type === START_CONSECUTIVE_TOGGLE ? action.id : state; +} + +export interface LogMonitorState { + initialScrollTop: number; + consecutiveToggleStartId: number | null | undefined; +} + +export default function reducer>( + props: LogMonitorProps, + state: Partial = {}, + action: LogMonitorAction +): LogMonitorState { + return { + initialScrollTop: initialScrollTop(props, state.initialScrollTop, action), + consecutiveToggleStartId: startConsecutiveToggle( + props, + state.consecutiveToggleStartId, + action + ), + }; +} diff --git a/packages/redux-devtools-log-monitor/tsconfig.json b/packages/redux-devtools-log-monitor/tsconfig.json new file mode 100644 index 00000000..7b7d1492 --- /dev/null +++ b/packages/redux-devtools-log-monitor/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.react.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/packages/redux-devtools/src/index.ts b/packages/redux-devtools/src/index.ts index 33ca13ff..76f04091 100644 --- a/packages/redux-devtools/src/index.ts +++ b/packages/redux-devtools/src/index.ts @@ -2,6 +2,8 @@ export { default as instrument, ActionCreators, ActionTypes, + PerformAction, + LiftedAction, LiftedState, } from 'redux-devtools-instrument'; export { default as persistState } from './persistState'; diff --git a/yarn.lock b/yarn.lock index 6e6c0774..180b96b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3045,7 +3045,7 @@ dependencies: "@babel/types" "^7.3.0" -"@types/base16@^1.0.2": +"@types/base16@*", "@types/base16@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/base16/-/base16-1.0.2.tgz#eb3a07db52309bfefb9ba010dfdb3c0784971f65" integrity sha512-oYO/U4VD1DavwrKuCSQWdLG+5K22SLPem2OQaHmFcQuwHoVeGC+JGVRji2MUqZUAIQZHEonOeVfAX09hYiLsdg== @@ -3340,6 +3340,13 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/redux-devtools-themes@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/redux-devtools-themes/-/redux-devtools-themes-1.0.0.tgz#4d22d3e8be499fc7eec220e020b0640d4bfb17bd" + integrity sha512-ul3x0MYM5Nzj57Fh9wINyHFne8vZL04RC4nWAUWLYcL105vHoa/oJyopuKOrQmqVmhqmDiL4c9FfLbUmIB7TWQ== + dependencies: + "@types/base16" "*" + "@types/serve-static@*": version "1.13.5" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.5.tgz#3d25d941a18415d3ab092def846e135a08bbcf53"