diff --git a/packages/redux-devtools-dock-monitor/.babelrc b/packages/redux-devtools-dock-monitor/.babelrc index 2d8ea82b..0d42ef44 100644 --- a/packages/redux-devtools-dock-monitor/.babelrc +++ b/packages/redux-devtools-dock-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-dock-monitor/.eslintignore b/packages/redux-devtools-dock-monitor/.eslintignore new file mode 100644 index 00000000..a65b4177 --- /dev/null +++ b/packages/redux-devtools-dock-monitor/.eslintignore @@ -0,0 +1 @@ +lib diff --git a/packages/redux-devtools-dock-monitor/.eslintrc.js b/packages/redux-devtools-dock-monitor/.eslintrc.js new file mode 100644 index 00000000..e98ddf18 --- /dev/null +++ b/packages/redux-devtools-dock-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-dock-monitor/package.json b/packages/redux-devtools-dock-monitor/package.json index 5bb00580..e53a621a 100644 --- a/packages/redux-devtools-dock-monitor/package.json +++ b/packages/redux-devtools-dock-monitor/package.json @@ -2,21 +2,6 @@ "name": "redux-devtools-dock-monitor", "version": "1.1.4", "description": "A resizable and movable dock for Redux DevTools monitors", - "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.git" - }, "keywords": [ "redux", "devtools", @@ -26,31 +11,50 @@ "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-dock-monitor", "bugs": { "url": "https://github.com/reduxjs/redux-devtools/issues" }, - "homepage": "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-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" + "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.git" }, - "peerDependencies": { - "react": "^0.14.9 || ^15.3.0 || ^16.0.0", - "redux-devtools": "^3.4.0" + "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": { - "babel-runtime": "^6.26.0", + "@types/prop-types": "^15.7.3", "parse-key": "^0.2.1", "prop-types": "^15.7.2", - "react-dock": "^0.2.4", - "react-pure-render": "^1.0.2" + "react-dock": "^0.2.4" + }, + "devDependencies": { + "@types/react": "^16.9.46", + "react": "^16.13.1", + "redux": "^4.0.5", + "redux-devtools": "^3.6.1" + }, + "peerDependencies": { + "@types/react": "^16.3.18", + "react": "^16.3.0", + "redux": "^3.4.0 || ^4.0.0", + "redux-devtools": "^3.4.0" } } diff --git a/packages/redux-devtools-dock-monitor/src/DockMonitor.js b/packages/redux-devtools-dock-monitor/src/DockMonitor.tsx similarity index 64% rename from packages/redux-devtools-dock-monitor/src/DockMonitor.js rename to packages/redux-devtools-dock-monitor/src/DockMonitor.tsx index 99427463..0849c70f 100644 --- a/packages/redux-devtools-dock-monitor/src/DockMonitor.js +++ b/packages/redux-devtools-dock-monitor/src/DockMonitor.tsx @@ -1,17 +1,49 @@ import React, { cloneElement, Children, Component } from 'react'; import PropTypes from 'prop-types'; import Dock from 'react-dock'; +import { Action, Dispatch } from 'redux'; +import { LiftedState, Monitor } from 'redux-devtools'; import { POSITIONS } from './constants'; import { toggleVisibility, changeMonitor, changePosition, changeSize, + DockMonitorAction, } from './actions'; -import reducer from './reducers'; +import reducer, { DockMonitorState } from './reducers'; import parseKey from 'parse-key'; -export default class DockMonitor extends Component { +interface KeyObject { + name: string; + ctrl: boolean; + meta: boolean; + shift: boolean; + alt: boolean; + sequence: string; +} + +export interface DockMonitorProps> + extends LiftedState { + defaultPosition: 'left' | 'top' | 'right' | 'bottom'; + defaultIsVisible: boolean; + defaultSize: number; + toggleVisibilityKey: string; + changePositionKey: string; + changeMonitorKey?: string; + fluid: boolean; + + dispatch: Dispatch; + + children: + | Monitor, unknown, Action> + | Monitor, unknown, Action>[]; +} + +export default class DockMonitor< + S, + A extends Action +> extends Component> { static update = reducer; static propTypes = { @@ -39,10 +71,8 @@ export default class DockMonitor extends Component { fluid: true, }; - constructor(props) { + constructor(props: DockMonitorProps) { super(props); - this.handleKeyDown = this.handleKeyDown.bind(this); - this.handleSizeChange = this.handleSizeChange.bind(this); const childrenCount = Children.count(props.children); if (childrenCount === 0) { @@ -71,7 +101,7 @@ export default class DockMonitor extends Component { window.removeEventListener('keydown', this.handleKeyDown); } - matchesKey(key, event) { + matchesKey(key: KeyObject | undefined, event: KeyboardEvent) { if (!key) { return false; } @@ -87,17 +117,17 @@ export default class DockMonitor extends Component { ); } - handleKeyDown(e) { + handleKeyDown = (e: KeyboardEvent) => { // Ignore regular keys when focused on a field // and no modifiers are active. if ( !e.ctrlKey && !e.metaKey && !e.altKey && - (e.target.tagName === 'INPUT' || - e.target.tagName === 'SELECT' || - e.target.tagName === 'TEXTAREA' || - e.target.isContentEditable) + ((e.target! as { tagName?: string }).tagName === 'INPUT' || + (e.target! as { tagName?: string }).tagName === 'SELECT' || + (e.target! as { tagName?: string }).tagName === 'TEXTAREA' || + (e.target! as { isContentEditable?: boolean }).isContentEditable) ) { return; } @@ -120,13 +150,20 @@ export default class DockMonitor extends Component { e.preventDefault(); this.props.dispatch(changeMonitor()); } - } + }; - handleSizeChange(requestedSize) { + handleSizeChange = (requestedSize: number) => { this.props.dispatch(changeSize(requestedSize)); - } + }; - renderChild(child, index, otherProps) { + renderChild( + child: Monitor, unknown, Action>, + index: number, + otherProps: Omit< + DockMonitorProps, + 'monitorState' | 'children' | 'fluid' + > + ) { const { monitorState } = this.props; const { childMonitorIndex, childMonitorStates } = monitorState; @@ -153,8 +190,23 @@ export default class DockMonitor extends Component { onSizeChange={this.handleSizeChange} dimMode="none" > - {Children.map(children, (child, index) => - this.renderChild(child, index, rest) + {Children.map( + children as + | Monitor< + S, + A, + LiftedState, + unknown, + Action + > + | Monitor< + S, + A, + LiftedState, + unknown, + Action + >[], + (child, index) => this.renderChild(child, index, rest) )} ); diff --git a/packages/redux-devtools-dock-monitor/src/actions.js b/packages/redux-devtools-dock-monitor/src/actions.js deleted file mode 100644 index d253293c..00000000 --- a/packages/redux-devtools-dock-monitor/src/actions.js +++ /dev/null @@ -1,20 +0,0 @@ -export const TOGGLE_VISIBILITY = - '@@redux-devtools-log-monitor/TOGGLE_VISIBILITY'; -export function toggleVisibility() { - return { type: TOGGLE_VISIBILITY }; -} - -export const CHANGE_POSITION = '@@redux-devtools-log-monitor/CHANGE_POSITION'; -export function changePosition() { - return { type: CHANGE_POSITION }; -} - -export const CHANGE_SIZE = '@@redux-devtools-log-monitor/CHANGE_SIZE'; -export function changeSize(size) { - return { type: CHANGE_SIZE, size: size }; -} - -export const CHANGE_MONITOR = '@@redux-devtools-log-monitor/CHANGE_MONITOR'; -export function changeMonitor() { - return { type: CHANGE_MONITOR }; -} diff --git a/packages/redux-devtools-dock-monitor/src/actions.ts b/packages/redux-devtools-dock-monitor/src/actions.ts new file mode 100644 index 00000000..42db490e --- /dev/null +++ b/packages/redux-devtools-dock-monitor/src/actions.ts @@ -0,0 +1,39 @@ +export const TOGGLE_VISIBILITY = + '@@redux-devtools-log-monitor/TOGGLE_VISIBILITY'; +interface ToggleVisibilityAction { + type: typeof TOGGLE_VISIBILITY; +} +export function toggleVisibility(): ToggleVisibilityAction { + return { type: TOGGLE_VISIBILITY }; +} + +export const CHANGE_POSITION = '@@redux-devtools-log-monitor/CHANGE_POSITION'; +interface ChangePositionAction { + type: typeof CHANGE_POSITION; +} +export function changePosition(): ChangePositionAction { + return { type: CHANGE_POSITION }; +} + +export const CHANGE_SIZE = '@@redux-devtools-log-monitor/CHANGE_SIZE'; +interface ChangeSizeAction { + type: typeof CHANGE_SIZE; + size: number; +} +export function changeSize(size: number): ChangeSizeAction { + return { type: CHANGE_SIZE, size: size }; +} + +export const CHANGE_MONITOR = '@@redux-devtools-log-monitor/CHANGE_MONITOR'; +interface ChangeMonitorAction { + type: typeof CHANGE_MONITOR; +} +export function changeMonitor(): ChangeMonitorAction { + return { type: CHANGE_MONITOR }; +} + +export type DockMonitorAction = + | ToggleVisibilityAction + | ChangePositionAction + | ChangeSizeAction + | ChangeMonitorAction; diff --git a/packages/redux-devtools-dock-monitor/src/constants.js b/packages/redux-devtools-dock-monitor/src/constants.js deleted file mode 100644 index e935427f..00000000 --- a/packages/redux-devtools-dock-monitor/src/constants.js +++ /dev/null @@ -1 +0,0 @@ -export const POSITIONS = ['left', 'top', 'right', 'bottom']; diff --git a/packages/redux-devtools-dock-monitor/src/constants.ts b/packages/redux-devtools-dock-monitor/src/constants.ts new file mode 100644 index 00000000..38c503d4 --- /dev/null +++ b/packages/redux-devtools-dock-monitor/src/constants.ts @@ -0,0 +1 @@ +export const POSITIONS = ['left', 'top', 'right', 'bottom'] as const; diff --git a/packages/redux-devtools-dock-monitor/src/index.js b/packages/redux-devtools-dock-monitor/src/index.js deleted file mode 100644 index 5b553f1d..00000000 --- a/packages/redux-devtools-dock-monitor/src/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './DockMonitor'; diff --git a/packages/redux-devtools-dock-monitor/src/index.ts b/packages/redux-devtools-dock-monitor/src/index.ts new file mode 100644 index 00000000..c950ac82 --- /dev/null +++ b/packages/redux-devtools-dock-monitor/src/index.ts @@ -0,0 +1,2 @@ +import DockMonitor from './DockMonitor'; +export default DockMonitor; diff --git a/packages/redux-devtools-dock-monitor/src/parse-key.ts b/packages/redux-devtools-dock-monitor/src/parse-key.ts new file mode 100644 index 00000000..e3cda606 --- /dev/null +++ b/packages/redux-devtools-dock-monitor/src/parse-key.ts @@ -0,0 +1,12 @@ +declare module 'parse-key' { + interface KeyObject { + name: string; + ctrl: boolean; + meta: boolean; + shift: boolean; + alt: boolean; + sequence: string; + } + + export default function parse(s: string): KeyObject; +} diff --git a/packages/redux-devtools-dock-monitor/src/reducers.js b/packages/redux-devtools-dock-monitor/src/reducers.ts similarity index 52% rename from packages/redux-devtools-dock-monitor/src/reducers.js rename to packages/redux-devtools-dock-monitor/src/reducers.ts index ff28f53e..eb2e4d91 100644 --- a/packages/redux-devtools-dock-monitor/src/reducers.js +++ b/packages/redux-devtools-dock-monitor/src/reducers.ts @@ -1,33 +1,64 @@ +import { Children } from 'react'; +import { Action } from 'redux'; import { CHANGE_MONITOR, CHANGE_POSITION, CHANGE_SIZE, + DockMonitorAction, TOGGLE_VISIBILITY, } from './actions'; import { POSITIONS } from './constants'; -import { Children } from 'react'; +import { DockMonitorProps } from './DockMonitor'; -function position(props, state = props.defaultPosition, action) { +export interface DockMonitorState { + position: 'left' | 'top' | 'right' | 'bottom'; + size: number; + isVisible: boolean; + childMonitorStates: unknown[]; + childMonitorIndex: number; +} + +function position>( + props: DockMonitorProps, + state = props.defaultPosition, + action: DockMonitorAction +) { return action.type === CHANGE_POSITION ? POSITIONS[(POSITIONS.indexOf(state) + 1) % POSITIONS.length] : state; } -function size(props, state = props.defaultSize, action) { +function size>( + props: DockMonitorProps, + state = props.defaultSize, + action: DockMonitorAction +) { return action.type === CHANGE_SIZE ? action.size : state; } -function isVisible(props, state = props.defaultIsVisible, action) { +function isVisible>( + props: DockMonitorProps, + state = props.defaultIsVisible, + action: DockMonitorAction +) { return action.type === TOGGLE_VISIBILITY ? !state : state; } -function childMonitorStates(props, state = [], action) { +function childMonitorStates>( + props: DockMonitorProps, + state: unknown[] = [], + action: DockMonitorAction +) { return Children.map(props.children, (child, index) => child.type.update(child.props, state[index], action) ); } -function childMonitorIndex(props, state = 0, action) { +function childMonitorIndex>( + props: DockMonitorProps, + state = 0, + action: DockMonitorAction +) { switch (action.type) { case CHANGE_MONITOR: return (state + 1) % Children.count(props.children); @@ -36,14 +67,22 @@ function childMonitorIndex(props, state = 0, action) { } } -export default function reducer(props, state = {}, action) { +export default function reducer>( + props: DockMonitorProps, + state: Partial = {}, + action: DockMonitorAction +): DockMonitorState { if (!state.childMonitorStates) { Children.forEach(props.children, (child, index) => { if (typeof child.type.update !== 'function') { // eslint-disable-next-line no-console console.error( `Child of with the index ${index} ` + - `(${child.type.displayName || child.type.name || child.type}) ` + + `(${ + child.type.displayName || + child.type.name || + ((child.type as unknown) as string) + }) ` + 'does not appear to be a valid Redux DevTools monitor.' ); } diff --git a/packages/redux-devtools-dock-monitor/tsconfig.json b/packages/redux-devtools-dock-monitor/tsconfig.json new file mode 100644 index 00000000..7b7d1492 --- /dev/null +++ b/packages/redux-devtools-dock-monitor/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.react.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +}