feat(redux-devtools-dock-monitor): convert to TypeScript (#609)

* stash

* more

* Fix
This commit is contained in:
Nathan Bierema 2020-08-26 09:22:53 -04:00 committed by GitHub
parent 47af8c98ce
commit b4ec7f86fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 234 additions and 85 deletions

View File

@ -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"]
}

View File

@ -0,0 +1 @@
lib

View 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'],
},
},
],
};

View File

@ -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 <dan.abramov@me.com> (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 <dan.abramov@me.com> (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"
}
}

View File

@ -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<S, A extends Action<unknown>>
extends LiftedState<S, A, DockMonitorState> {
defaultPosition: 'left' | 'top' | 'right' | 'bottom';
defaultIsVisible: boolean;
defaultSize: number;
toggleVisibilityKey: string;
changePositionKey: string;
changeMonitorKey?: string;
fluid: boolean;
dispatch: Dispatch<DockMonitorAction>;
children:
| Monitor<S, A, LiftedState<S, A, unknown>, unknown, Action<unknown>>
| Monitor<S, A, LiftedState<S, A, unknown>, unknown, Action<unknown>>[];
}
export default class DockMonitor<
S,
A extends Action<unknown>
> extends Component<DockMonitorProps<S, A>> {
static update = reducer;
static propTypes = {
@ -39,10 +71,8 @@ export default class DockMonitor extends Component {
fluid: true,
};
constructor(props) {
constructor(props: DockMonitorProps<S, A>) {
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<S, A, LiftedState<S, A, unknown>, unknown, Action<unknown>>,
index: number,
otherProps: Omit<
DockMonitorProps<S, A>,
'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<S, A, unknown>,
unknown,
Action<unknown>
>
| Monitor<
S,
A,
LiftedState<S, A, unknown>,
unknown,
Action<unknown>
>[],
(child, index) => this.renderChild(child, index, rest)
)}
</Dock>
);

View File

@ -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 };
}

View File

@ -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;

View File

@ -1 +0,0 @@
export const POSITIONS = ['left', 'top', 'right', 'bottom'];

View File

@ -0,0 +1 @@
export const POSITIONS = ['left', 'top', 'right', 'bottom'] as const;

View File

@ -1 +0,0 @@
export default from './DockMonitor';

View File

@ -0,0 +1,2 @@
import DockMonitor from './DockMonitor';
export default DockMonitor;

View File

@ -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;
}

View File

@ -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<S, A extends Action<unknown>>(
props: DockMonitorProps<S, A>,
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<S, A extends Action<unknown>>(
props: DockMonitorProps<S, A>,
state = props.defaultSize,
action: DockMonitorAction
) {
return action.type === CHANGE_SIZE ? action.size : state;
}
function isVisible(props, state = props.defaultIsVisible, action) {
function isVisible<S, A extends Action<unknown>>(
props: DockMonitorProps<S, A>,
state = props.defaultIsVisible,
action: DockMonitorAction
) {
return action.type === TOGGLE_VISIBILITY ? !state : state;
}
function childMonitorStates(props, state = [], action) {
function childMonitorStates<S, A extends Action<unknown>>(
props: DockMonitorProps<S, A>,
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<S, A extends Action<unknown>>(
props: DockMonitorProps<S, A>,
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<S, A extends Action<unknown>>(
props: DockMonitorProps<S, A>,
state: Partial<DockMonitorState> = {},
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 <DockMonitor> 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.'
);
}

View File

@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.react.base.json",
"compilerOptions": {
"outDir": "lib"
},
"include": ["src"]
}