Repository navigation
Expand file tree
/
Copy pathcursorBack.ts
More file actions
94 lines (79 loc) · 3.8 KB
/
Copy pathcursorBack.ts
File metadata and controls
94 lines (79 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import State from '../@types/State'
import Thunk from '../@types/Thunk'
import addMulticursor from '../actions/addMulticursor'
import cursorHistory from '../actions/cursorHistory'
import removeMulticursor from '../actions/removeMulticursor'
import searchReducer from '../actions/search'
import setCursor from '../actions/setCursor'
import expandThoughts from '../selectors/expandThoughts'
import hasMulticursor from '../selectors/hasMulticursor'
import { registerActionMetadata } from '../util/actionMetadata.registry'
import isAbsolute from '../util/isAbsolute'
import parentOf from '../util/parentOf'
import reducerFlow from '../util/reducerFlow'
import toggleAbsoluteContext from './toggleAbsoluteContext'
/** Replaces the multiselect with the parents of each selected thought. Parents shared by multiple selected thoughts are selected once, since the multicursor set is keyed by path. */
const multicursorBack = (state: State): State => {
const paths = Object.values(state.multicursors)
// Root-level thoughts contribute no parent, since the root cannot be selected.
const backPaths = paths.filter(path => path.length > 1).map(parentOf)
// do nothing if all selected thoughts are at the root level, so that an extra Back gesture does not destroy the selection
if (backPaths.length === 0) return state
const stateNew = reducerFlow([
// Deselecting before selecting is safe within a single action, since multicursorAlertMiddleware only sees the
// final state and thus never a momentarily empty multiselect (which would close the Command Center on mobile).
// A selected thought that is also the parent of another selected thought is deselected and reselected.
...paths.map(path => removeMulticursor({ path })),
...backPaths.map(path => addMulticursor({ path })),
])(state)
return {
...stateNew,
// Selected thoughts are kept collapsed by expandThoughts, so expansion must be recalculated for the newly
// selected parents to collapse the previously selected children.
// https://github.com/cybersemics/em/issues/4738
expanded: expandThoughts(stateNew, stateNew.cursor),
}
}
/** Moves the cursor up one level. When thoughts are selected, replaces the selection with their parents instead of moving the cursor. */
const cursorBack = (state: State): State => {
if (hasMulticursor(state)) return multicursorBack(state)
const { cursor: cursorOld, isKeyboardOpen, search, rootContext } = state
const isAbsoluteRoot = isAbsolute(rootContext)
const cursorNew = cursorOld && parentOf(cursorOld)
return reducerFlow(
// if there is a cursor, move it to its parent
cursorOld
? [
// move cursor back
setCursor({
// offset shouldn't be null if we want useEditMode to set the selection to the new thought
offset: 0,
path: cursorNew!.length > 0 ? cursorNew : null,
isKeyboardOpen,
preserveMulticursor: true,
}),
// append to cursor history to allow 'forward' gesture
cursorHistory({ cursor: cursorOld }),
]
: // if there is no cursor and isAbsoluteRoot is active, toggle the context
// else of search is active, close the search
isAbsoluteRoot
? [toggleAbsoluteContext]
: search === ''
? [
// close the search
searchReducer({ value: null }),
// restore the cursor
state.cursorBeforeSearch ? setCursor({ path: state.cursorBeforeSearch, isKeyboardOpen }) : null,
]
: [],
)(state)
}
/** Action-creator for cursorBack. */
export const cursorBackActionCreator = (): Thunk => dispatch => dispatch({ type: 'cursorBack' })
export default cursorBack
// Register this action's metadata
registerActionMetadata('cursorBack', {
undoable: true,
isNavigation: true,
})