redux-devtools/packages/redux-devtools-chart-monitor/src/ChartMonitor.tsx

209 lines
5.0 KiB
TypeScript
Raw Normal View History

import React, { CSSProperties, PureComponent } from 'react';
import PropTypes from 'prop-types';
import * as themes from 'redux-devtools-themes';
import { ActionCreators, LiftedAction, LiftedState } from 'redux-devtools';
import deepmerge from 'deepmerge';
import { Action, Dispatch } from 'redux';
import { Base16Theme } from 'react-base16-styling';
import reducer, { ChartMonitorState } from './reducers';
import Chart, { Props } from './Chart';
import { Primitive } from 'd3';
import { NodeWithId } from 'd3-state-visualizer/lib/charts/tree/tree';
// eslint-disable-next-line @typescript-eslint/unbound-method
const { reset, rollback, commit, sweep, toggleAction } = ActionCreators;
const styles: { container: CSSProperties } = {
container: {
fontFamily: 'monaco, Consolas, Lucida Console, monospace',
position: 'relative',
overflowY: 'hidden',
width: '100%',
height: '100%',
minWidth: 300,
},
};
function invertColors(theme: Base16Theme) {
return {
...theme,
base00: theme.base07,
base01: theme.base06,
base02: theme.base05,
base03: theme.base04,
base04: theme.base03,
base05: theme.base02,
base06: theme.base01,
base07: theme.base00,
};
}
export interface ChartMonitorProps<S, A extends Action<unknown>>
extends LiftedState<S, A, ChartMonitorState> {
dispatch: Dispatch<LiftedAction<S, A, ChartMonitorState>>;
preserveScrollTop: boolean;
select: (state: S) => unknown;
theme: keyof typeof themes | Base16Theme;
invertTheme: boolean;
state: S | null;
isSorted: boolean;
heightBetweenNodesCoeff: number;
widthBetweenNodesCoeff: number;
onClickText: (datum: NodeWithId) => void;
tooltipOptions: unknown;
style: {
width: number;
height: number;
node: {
colors: {
default: string;
collapsed: string;
parent: string;
};
radius: number;
};
text: {
colors: {
default: string;
hover: string;
};
};
};
defaultIsVisible?: boolean;
}
class ChartMonitor<S, A extends Action<unknown>> extends PureComponent<
ChartMonitorProps<S, A>
> {
static update = reducer;
static propTypes = {
dispatch: PropTypes.func,
computedStates: PropTypes.array,
currentStateIndex: PropTypes.number,
actionsById: PropTypes.object,
stagedActionIds: PropTypes.array,
skippedActionIds: PropTypes.array,
monitorState: PropTypes.shape({
initialScrollTop: PropTypes.number,
}),
preserveScrollTop: PropTypes.bool,
select: PropTypes.func.isRequired,
theme: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
invertTheme: PropTypes.bool,
};
static defaultProps = {
select: (state: unknown) => state,
theme: 'nicinabox',
preserveScrollTop: true,
invertTheme: false,
};
handleRollback = () => {
this.props.dispatch(rollback());
};
handleSweep = () => {
this.props.dispatch(sweep());
};
handleCommit = () => {
this.props.dispatch(commit());
};
handleToggleAction = (id: number) => {
this.props.dispatch(toggleAction(id));
};
handleReset = () => {
this.props.dispatch(reset());
};
getTheme() {
const { theme, invertTheme } = this.props;
if (typeof theme !== 'string') {
return invertTheme ? invertColors(theme) : theme;
}
if (typeof themes[theme] !== 'undefined') {
return invertTheme ? invertColors(themes[theme]) : themes[theme];
}
console.warn(
'DevTools theme ' + theme + ' not found, defaulting to nicinabox'
);
return invertTheme ? invertColors(themes.nicinabox) : themes.nicinabox;
}
getChartStyle() {
const theme = this.getTheme();
return {
width: '100%',
height: '100%',
node: {
colors: {
default: theme.base0B,
collapsed: theme.base0B,
parent: theme.base0E,
},
radius: 7,
},
text: {
colors: {
default: theme.base0D,
hover: theme.base06,
},
},
};
}
getChartOptions(props = this.props): Props<S, A> {
const { computedStates } = props;
const theme = this.getTheme();
const tooltipOptions = {
disabled: false,
offset: { left: 30, top: 10 },
indentationSize: 2,
style: {
'background-color': theme.base06,
opacity: '0.7',
'border-radius': '5px',
padding: '5px',
},
};
const defaultOptions = {
state: computedStates.length
? computedStates[props.currentStateIndex].state
: null,
isSorted: false,
heightBetweenNodesCoeff: 1,
widthBetweenNodesCoeff: 1.3,
tooltipOptions,
style: (this.getChartStyle() as unknown) as
| { [key: string]: Primitive }
| undefined,
};
return deepmerge(defaultOptions, props);
}
render() {
const theme = this.getTheme();
const ChartAsAny = Chart as any;
return (
<div style={{ ...styles.container, backgroundColor: theme.base07 }}>
<ChartAsAny {...this.getChartOptions()} />
</div>
);
}
}
export default ChartMonitor;