redux-saga-devtools
Version:
Monitor and UI for redux-saga
167 lines (146 loc) • 4.78 kB
JavaScript
import PropTypes from 'prop-types';
import React from 'react';
import styled, { css } from 'styled-components'
import { connect } from 'react-redux'
import { matchCurrentAction } from '../../store/selectors'
import {
KEY_ARROW_DOWN,
KEY_ARROW_UP,
KEY_ARROW_LEFT,
KEY_ARROW_RIGHT
} from '../../utils'
import ListView from '../../components/ListView'
import EffectEntry from '../EffectEntry'
const EffectListContainer = styled.div`
outline: none;
cursor: default;
user-select: none;
`
const cssMatchAction = css`
background-color: rgb(241, 248, 233);
border-top: 1px solid rgb(220, 237, 200);
border-bottom: 1px solid rgb(220, 237, 200);
margin-top: -1px;
`
class EffectList extends React.Component {
state = {
collapsedEffects: {}
}
isCollapsed = effectId => {
return this.state.collapsedEffects[effectId]
}
collapseEffect = (effectId, collapsed) => {
this.setState(state => ({
collapsedEffects: {
...state.collapsedEffects,
[effectId]: collapsed !== undefined ? collapsed : !state.collapsedEffects[effectId]
}
}))
}
onKeyDown = e => {
if(e.which === KEY_ARROW_DOWN) {
this.selectDown(this.props.selectedEffectId)
e.preventDefault()
} else if(e.which === KEY_ARROW_UP) {
this.selectUp(this.props.selectedEffectId)
e.preventDefault()
} else if(e.which === KEY_ARROW_LEFT) {
this.selectLeft(this.props.selectedEffectId)
e.preventDefault()
}
else if(e.which === KEY_ARROW_RIGHT) {
this.selectRight(this.props.selectedEffectId)
e.preventDefault()
}
}
selectLeft = effectId => {
if(!this.isCollapsed(effectId) && this.props.effectsByParentId[effectId]) {
this.collapseEffect(effectId, true)
} else {
const isTop = (
effectId === this.state.pinnedEffectId ||
this.props.rootEffectIds.indexOf(effectId) >= 0
)
if(!isTop) {
const parentId = this.props.effectsById[effectId].parentEffectId
this.props.onSelectionChange(parentId)
}
}
}
selectRight = effectId => {
if(this.isCollapsed(effectId)) {
this.collapseEffect(effectId, false)
} else {
if(this.props.effectsByParentId[effectId]) {
this.selectDown(effectId)
}
}
}
selectUp = effectId => {
const idx = this.visuallyOrderedEffects.indexOf(effectId)
if(idx > 0) {
const prevEffect = this.visuallyOrderedEffects[idx-1]
this.props.onSelectionChange(prevEffect)
}
}
selectDown = (effectId, onlyChild) => {
const idx = this.visuallyOrderedEffects.indexOf(effectId)
if(idx < this.visuallyOrderedEffects.length - 1) {
const nextEffect = this.visuallyOrderedEffects[idx+1]
this.props.onSelectionChange(nextEffect)
}
}
renderEffectList(effectIds, elems, depth = 0, prefix='eff') {
return effectIds.forEach((effectId, idx) => {
this.visuallyOrderedEffects.push(effectId)
const effect = this.props.effectsById[effectId]
const childsEffectsId = this.props.effectsByParentId[effectId]
const hasChildren = childsEffectsId && childsEffectsId.length
const css = matchCurrentAction(this.props.state, effectId) ? cssMatchAction : null
elems.push(
<EffectEntry
id={prefix + String(effectId)}
css={css}
depth={depth}
effectId={effectId}
selected={this.props.selectedEffectId === effectId}
pinned={this.props.pinnedEffectId === effectId}
collapsed={this.isCollapsed(effectId)}
onCollapse={this.collapseEffect}
onPin={this.props.onPin}
onUnpin={this.props.onUnpin}
onSelect={this.props.onSelectionChange}
/>
)
if(!this.isCollapsed(effectId) && hasChildren) {
this.renderEffectList(childsEffectsId, elems, depth + 1, prefix)
}
})
}
render() {
const elems = []
this.visuallyOrderedEffects = []
const pinnedEffectId = this.props.pinnedEffectId
const rootEffectIds = pinnedEffectId < 0 ? this.props.rootEffectIds : [pinnedEffectId]
this.renderEffectList(rootEffectIds, elems, 0)
return (
<EffectListContainer tabIndex='0' onKeyDown={this.onKeyDown} >
<ListView nodes={elems} />
</EffectListContainer>
)
}
}
EffectList.propTypes = {
// passed by the parent Component
selectedEffectId: PropTypes.number,
onSelectionChange: PropTypes.func.isRequired,
rootEffectIds: PropTypes.array.isRequired,
// Injected by redux
effectsById: PropTypes.object.isRequired,
effectsByParentId: PropTypes.object.isRequired,
}
export default connect( state => ({
state: state,
effectsById: state.effectsById,
effectsByParentId: state.effectsByParentId
}))(EffectList)