139 lines
4.1 KiB
TypeScript
139 lines
4.1 KiB
TypeScript
import * as q from '../../../backend/src/Model'
|
|
import { AppState } from '../reducers'
|
|
import { Dispatch } from 'redux'
|
|
import { selectTopic } from './Tree'
|
|
import { SettingsState } from '../reducers/Settings'
|
|
import { sortedNodes } from '../sortedNodes'
|
|
import { TopicViewModel } from '../model/TopicViewModel'
|
|
|
|
export const moveSelectionUpOrDownwards =
|
|
(direction: 'next' | 'previous') =>
|
|
(dispatch: Dispatch<any>, getState: () => AppState): any => {
|
|
const state = getState()
|
|
const selected = state.tree.get('selectedTopic')
|
|
const tree = state.tree.get('tree')
|
|
|
|
if (!selected || !tree) {
|
|
if (tree) {
|
|
dispatch(selectTopic(tree))
|
|
}
|
|
return
|
|
}
|
|
const nextTreeNode = nextVisibleElementInTree(state.settings, tree, selected, direction)
|
|
if (nextTreeNode && nextTreeNode.viewModel) {
|
|
dispatch(selectTopic(nextTreeNode))
|
|
}
|
|
}
|
|
|
|
export const moveInward =
|
|
() =>
|
|
(dispatch: Dispatch<any>, getState: () => AppState): any => {
|
|
const state = getState()
|
|
const selected = state.tree.get('selectedTopic')
|
|
if (!selected || !selected.viewModel) {
|
|
return
|
|
}
|
|
|
|
if (!selected.viewModel.isExpanded() && selected.edgeCount() > 0) {
|
|
selected.viewModel.setExpanded(true, true)
|
|
} else {
|
|
dispatch(moveSelectionUpOrDownwards('next'))
|
|
}
|
|
}
|
|
|
|
export const moveOutward =
|
|
() =>
|
|
(dispatch: Dispatch<any>, getState: () => AppState): any => {
|
|
const state = getState()
|
|
const selected = state.tree.get('selectedTopic')
|
|
if (!selected || !selected.viewModel) {
|
|
return
|
|
}
|
|
|
|
if (selected.viewModel.isExpanded() && selected.edgeCount() > 0) {
|
|
selected.viewModel.setExpanded(false, true)
|
|
} else {
|
|
dispatch(moveSelectionUpOrDownwards('previous'))
|
|
}
|
|
}
|
|
|
|
function isTreeNodeVisible(treeNode: q.TreeNode<any>) {
|
|
return Boolean(treeNode.viewModel)
|
|
}
|
|
|
|
function nextVisibleElementInTree(
|
|
settings: SettingsState,
|
|
tree: q.Tree<TopicViewModel>,
|
|
node: q.TreeNode<TopicViewModel>,
|
|
direction: 'next' | 'previous'
|
|
): q.TreeNode<TopicViewModel> | undefined {
|
|
if (direction === 'next') {
|
|
return findNextNodeDownward(settings, node)
|
|
} else {
|
|
return findNextNodeUpward(settings, node)
|
|
}
|
|
}
|
|
|
|
/** Not very efficient but easy to implement, complexity should not be an issue here */
|
|
function findNextNodeUpward(
|
|
settings: SettingsState,
|
|
treeNode: q.TreeNode<TopicViewModel>
|
|
): q.TreeNode<TopicViewModel> | undefined {
|
|
const parent = treeNode.sourceEdge && treeNode.sourceEdge.source
|
|
if (!parent) {
|
|
return undefined
|
|
}
|
|
|
|
const neighborNodes = sortedNodes(settings, parent)
|
|
const nodeIdx = neighborNodes.findIndex(n => n.path() === treeNode.path())
|
|
if (nodeIdx === 0) {
|
|
return parent
|
|
}
|
|
const upwardNeighbor = neighborNodes[nodeIdx - 1]
|
|
if (upwardNeighbor) {
|
|
return lastVisibleChild(settings, upwardNeighbor)
|
|
} else {
|
|
return findNextNodeUpward(settings, parent)
|
|
}
|
|
}
|
|
|
|
function lastVisibleChild(settings: SettingsState, treeNode: q.TreeNode<TopicViewModel>): q.TreeNode<TopicViewModel> {
|
|
const nodes = sortedNodes(settings, treeNode).filter(isTreeNodeVisible)
|
|
if (nodes.length === 0) {
|
|
return treeNode
|
|
}
|
|
return lastVisibleChild(settings, nodes[nodes.length - 1])
|
|
}
|
|
|
|
function findNextNodeDownward(
|
|
settings: SettingsState,
|
|
treeNode: q.TreeNode<TopicViewModel>
|
|
): q.TreeNode<TopicViewModel> | undefined {
|
|
const children = sortedNodes(settings, treeNode).filter(isTreeNodeVisible)
|
|
const firstChild = children[0]
|
|
if (firstChild) {
|
|
return firstChild
|
|
}
|
|
|
|
return findNextNodeDownwardNeighbor(settings, treeNode)
|
|
}
|
|
|
|
function findNextNodeDownwardNeighbor(
|
|
settings: SettingsState,
|
|
treeNode: q.TreeNode<TopicViewModel>
|
|
): q.TreeNode<TopicViewModel> | undefined {
|
|
const parent = treeNode.sourceEdge && treeNode.sourceEdge.source
|
|
if (!parent) {
|
|
return undefined
|
|
}
|
|
|
|
const neighborNodes = sortedNodes(settings, parent).filter(isTreeNodeVisible)
|
|
const nodeIdx = neighborNodes.findIndex(n => n.path() === treeNode.path())
|
|
const downwardNeighbor = neighborNodes[nodeIdx + 1]
|
|
if (downwardNeighbor) {
|
|
return downwardNeighbor
|
|
} else {
|
|
return findNextNodeDownwardNeighbor(settings, parent)
|
|
}
|
|
}
|