Add tree navigation via arrow keys

This commit is contained in:
Thomas Nordquist
2019-06-25 01:39:31 +02:00
parent d054e64568
commit f4051b4cdf
9 changed files with 179 additions and 58 deletions

View File

@@ -1,13 +1,14 @@
import * as q from '../../../../backend/src/Model'
import * as React from 'react'
import React from 'react'
import TreeNode from './TreeNode'
import { AppState } from '../../reducers'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Record } from 'immutable'
import { SettingsState } from '../../reducers/Settings'
import { TopicViewModel } from '../../model/TopicViewModel'
import { treeActions } from '../../actions'
import { useGlobalKeyEventHandler } from '../../effects/useGlobalKeyEventHandler'
import { KeyCodes } from '../../utils/KeyCodes'
const MovingAverage = require('moving-average')
@@ -20,16 +21,27 @@ interface Props {
actions: typeof treeActions
connectionId?: string
tree?: q.Tree<TopicViewModel>
filter: string
host?: string
paused: boolean
settings: Record<SettingsState>
settings: SettingsState
}
interface State {
lastUpdate: number
}
function ArrowKeyHandler(props: {
action: (direction: 'next' | 'previous') => any
leftAction: () => void
rightAction: () => void
}) {
useGlobalKeyEventHandler(KeyCodes.arrow_down, () => props.action('next'), [props.action])
useGlobalKeyEventHandler(KeyCodes.arrow_up, () => props.action('previous'), [props.action])
useGlobalKeyEventHandler(KeyCodes.arrow_right, props.rightAction, [props.action])
useGlobalKeyEventHandler(KeyCodes.arrow_left, props.leftAction, [props.action])
return <div />
}
class TreeComponent extends React.PureComponent<Props, State> {
private updateTimer?: any
private perf: number = 0
@@ -99,7 +111,7 @@ class TreeComponent extends React.PureComponent<Props, State> {
}
public render() {
const { tree, filter } = this.props
const { tree } = this.props
if (!tree) {
return null
}
@@ -116,6 +128,11 @@ class TreeComponent extends React.PureComponent<Props, State> {
return (
<div style={style}>
<ArrowKeyHandler
action={this.props.actions.moveSelectionUpOrDownwards}
leftAction={this.props.actions.moveOutward}
rightAction={this.props.actions.moveInward}
/>
<TreeNode
key={tree.hash()}
isRoot={true}

View File

@@ -2,11 +2,9 @@ import * as q from '../../../../backend/src/Model'
import * as React from 'react'
import TreeNodeSubnodes from './TreeNodeSubnodes'
import TreeNodeTitle from './TreeNodeTitle'
import { Record } from 'immutable'
import { SettingsState } from '../../reducers/Settings'
import { Theme, withStyles } from '@material-ui/core/styles'
import { TopicViewModel } from '../../model/TopicViewModel'
const debounce = require('lodash.debounce')
declare var performance: any
@@ -61,7 +59,7 @@ interface Props {
lastUpdate: number
didSelectTopic: any
theme: Theme
settings: Record<SettingsState>
settings: SettingsState
}
interface State {
@@ -72,9 +70,7 @@ interface State {
class TreeNodeComponent extends React.Component<Props, State> {
private animationDirty: boolean = false
private cssAnimationWasSetAt?: number
private willUpdateTime: number = performance.now()
private nodeRef?: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>()
@@ -94,16 +90,24 @@ class TreeNodeComponent extends React.Component<Props, State> {
private addSubscriber(treeNode: q.TreeNode<TopicViewModel>) {
treeNode.viewModel = new TopicViewModel()
treeNode.viewModel.change.subscribe(this.viewStateHasChanged)
treeNode.viewModel.selectionChange.subscribe(this.selectionDidChange)
treeNode.viewModel.expandedChange.subscribe(this.expandedDidChange)
}
private viewStateHasChanged = () => {
private selectionDidChange = () => {
this.props.treeNode.viewModel && this.setState({ selected: this.props.treeNode.viewModel.isSelected() })
}
private expandedDidChange = () => {
this.props.treeNode.viewModel && this.setState({ collapsedOverride: !this.props.treeNode.viewModel.isExpanded() })
}
private removeSubscriber(treeNode: q.TreeNode<TopicViewModel>) {
treeNode.viewModel && treeNode.viewModel.change.unsubscribe(this.viewStateHasChanged)
treeNode.viewModel = undefined
if (treeNode.viewModel) {
treeNode.viewModel.selectionChange.unsubscribe(this.selectionDidChange)
treeNode.viewModel.expandedChange.unsubscribe(this.expandedDidChange)
treeNode.viewModel = undefined
}
}
private stateHasChanged(newState: State) {
@@ -160,6 +164,9 @@ class TreeNodeComponent extends React.Component<Props, State> {
}
private renderNodes() {
const isCollapsed = this.collapsed()
this.props.treeNode.viewModel && this.props.treeNode.viewModel.setExpanded(!isCollapsed, false)
if (this.collapsed()) {
return null
}

View File

@@ -1,8 +1,8 @@
import * as q from '../../../../backend/src/Model'
import * as React from 'react'
import TreeNode from './TreeNode'
import { Record } from 'immutable'
import { SettingsState, TopicOrder } from '../../reducers/Settings'
import { SettingsState } from '../../reducers/Settings'
import { sortedNodes } from '../../sortedNodes'
import { Theme, withStyles } from '@material-ui/core'
import { TopicViewModel } from '../../model/TopicViewModel'
@@ -14,7 +14,7 @@ export interface Props {
lastUpdate: number
selectedTopic?: q.TreeNode<TopicViewModel>
didSelectTopic: any
settings: Record<SettingsState>
settings: SettingsState
}
interface State {
@@ -28,26 +28,6 @@ class TreeNodeSubnodes extends React.Component<Props, State> {
this.state = { alreadyAdded: 10 }
}
private sortedNodes(): Array<q.TreeNode<TopicViewModel>> {
const { settings, treeNode } = this.props
const topicOrder = settings.get('topicOrder')
let edges = treeNode.edgeArray
if (topicOrder === TopicOrder.abc) {
edges = edges.sort((a, b) => a.name.localeCompare(b.name))
}
let nodes = edges.map(edge => edge.target)
if (topicOrder === TopicOrder.messages) {
nodes = nodes.sort((a, b) => b.leafMessageCount() - a.leafMessageCount())
}
if (topicOrder === TopicOrder.topics) {
nodes = nodes.sort((a, b) => b.childTopicCount() - a.childTopicCount())
}
return nodes
}
private renderMore() {
this.renderMoreAnimationFrame = (window as any).requestIdleCallback(
() => {
@@ -71,7 +51,7 @@ class TreeNodeSubnodes extends React.Component<Props, State> {
this.renderMore()
}
const nodes = this.sortedNodes().slice(0, this.state.alreadyAdded)
const nodes = sortedNodes(this.props.settings, this.props.treeNode).slice(0, this.state.alreadyAdded)
const listItems = nodes.map(node => {
return (
<TreeNode