Skip to content

Commit 8c20dac

Browse files
authored
feat: add collapse all notes and view in tree buttons (#116)
1 parent 7301ff3 commit 8c20dac

File tree

4 files changed

+102
-22
lines changed

4 files changed

+102
-22
lines changed

components/icon-button.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
ExternalLinkIcon,
1919
BookmarkAltIcon,
2020
PuzzleIcon,
21+
ChevronDoubleUpIcon
2122
} from '@heroicons/react/outline'
2223

2324
export const ICONS = {
@@ -38,6 +39,7 @@ export const ICONS = {
3839
ExternalLink: ExternalLinkIcon,
3940
BookmarkAlt: BookmarkAltIcon,
4041
Puzzle: PuzzleIcon,
42+
ChevronDoubleUp: ChevronDoubleUpIcon,
4143
}
4244

4345
const IconButton = forwardRef<

components/note-nav.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import PortalState from 'libs/web/state/portal'
1212
import { NOTE_SHARED } from 'libs/shared/meta'
1313
import useI18n from 'libs/web/hooks/use-i18n'
1414
import NavButtonGroup from './nav-button-group'
15+
import { EyeIcon } from '@heroicons/react/outline'
1516

1617
const MenuButton = () => {
1718
const { sidebar } = UIState.useContainer()
@@ -37,7 +38,7 @@ const NoteNav = () => {
3738
const { t } = useI18n()
3839
const { note, loading } = NoteState.useContainer()
3940
const { ua } = UIState.useContainer()
40-
const { getPaths } = NoteTreeState.useContainer()
41+
const { getPaths, showItem, checkItemIsShown } = NoteTreeState.useContainer()
4142
const { share, menu } = PortalState.useContainer()
4243

4344
const handleClickShare = useCallback(
@@ -58,6 +59,11 @@ const NoteNav = () => {
5859
[note, menu]
5960
)
6061

62+
const handleClickOpenInTree = useCallback(() => {
63+
if (!note) return
64+
showItem(note);
65+
}, [note, showItem])
66+
6167
return (
6268
<nav
6369
className={classNames(
@@ -92,14 +98,27 @@ const NoteNav = () => {
9298
</div>
9399
</Tooltip>
94100
))}
95-
<Tooltip title={note.title}>
96-
<span
97-
className="title block text-gray-600 text-sm truncate select-none"
98-
aria-current="page"
99-
>
100-
{note.title}
101-
</span>
102-
</Tooltip>
101+
<span>
102+
<Tooltip title={note.title}>
103+
<span
104+
className="title inline-block text-gray-600 text-sm truncate select-none align-middle"
105+
aria-current="page"
106+
>
107+
{note.title}
108+
</span>
109+
</Tooltip>
110+
{!checkItemIsShown(note) && (
111+
<Tooltip title={t('Show note in tree')}>
112+
<span>
113+
<EyeIcon
114+
width="20"
115+
className="inline-block cursor-pointer ml-1"
116+
onClick={handleClickOpenInTree}
117+
/>
118+
</span>
119+
</Tooltip>
120+
)}
121+
</span>
103122
</Breadcrumbs>
104123
)}
105124
<style jsx>

components/sidebar/sidebar-list.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import router from 'next/router'
66
import HotkeyTooltip from 'components/hotkey-tooltip'
77
import IconButton from 'components/icon-button'
88
import useI18n from 'libs/web/hooks/use-i18n'
9-
import { CircularProgress } from '@material-ui/core'
9+
import { CircularProgress, Tooltip } from '@material-ui/core'
1010
import { Favorites } from './favorites'
1111

1212
const SideBarList = () => {
@@ -16,6 +16,7 @@ const SideBarList = () => {
1616
moveItem,
1717
mutateItem,
1818
initLoaded,
19+
collapseAllItems
1920
} = NoteTreeState.useContainer()
2021

2122
const onExpand = useCallback(
@@ -66,6 +67,13 @@ const SideBarList = () => {
6667
<CircularProgress className="ml-4" size="14px" color="inherit" />
6768
)}
6869
</div>
70+
<Tooltip title={t('Collapse all pages')}>
71+
<IconButton
72+
icon="ChevronDoubleUp"
73+
onClick={collapseAllItems}
74+
className="text-gray-700 w-5 h-5 md:w-5 md:h-5"
75+
></IconButton>
76+
</Tooltip>
6977
<HotkeyTooltip
7078
text={t('Create page')}
7179
commandKey

libs/web/state/tree.ts

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { cloneDeep, forEach, isEmpty, map } from 'lodash'
1+
import { cloneDeep, forEach, isEmpty, map, reduce } from 'lodash'
22
import { genId } from 'libs/shared/id'
33
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
44
import { createContainer } from 'unstated-next'
@@ -19,6 +19,23 @@ import { uiCache } from '../cache'
1919

2020
const TREE_CACHE_KEY = 'tree'
2121

22+
const findParentTreeItems = (tree: TreeModel, note: NoteModel) => {
23+
const parents = [] as TreeItemModel[]
24+
25+
let tempNote = note
26+
while (tempNote.pid && tempNote.pid !== ROOT_ID) {
27+
const curData = tree.items[tempNote.pid]
28+
if (curData?.data) {
29+
tempNote = curData.data
30+
parents.push(curData)
31+
} else {
32+
break
33+
}
34+
}
35+
36+
return parents
37+
}
38+
2239
const useNoteTree = (initData: TreeModel = DEFAULT_TREE) => {
2340
const { mutate, loading, fetch: fetchTree } = useTreeAPI()
2441
const [tree, setTree] = useState<TreeModel>(initData)
@@ -146,21 +163,52 @@ const useNoteTree = (initData: TreeModel = DEFAULT_TREE) => {
146163

147164
const getPaths = useCallback((note: NoteModel) => {
148165
const tree = treeRef.current
149-
const paths = [] as NoteModel[]
150-
151-
while (note.pid && note.pid !== ROOT_ID) {
152-
const curData = tree.items[note.pid]?.data
153-
if (curData) {
154-
note = curData
155-
paths.push(note)
156-
} else {
157-
break
166+
return findParentTreeItems(tree, note).map((listItem) => listItem.data!)
167+
}, [])
168+
169+
const setItemsExpandState = useCallback(
170+
async (items: TreeItemModel[], newValue: boolean) => {
171+
const newTree = reduce(
172+
items,
173+
(tempTree, item) =>
174+
TreeActions.mutateItem(tempTree, item.id, { isExpanded: newValue }),
175+
treeRef.current
176+
)
177+
setTree(newTree)
178+
179+
for (const item of items) {
180+
await mutate({
181+
action: 'mutate',
182+
data: {
183+
isExpanded: newValue,
184+
id: item.id,
185+
},
186+
})
158187
}
159-
}
188+
},
189+
[mutate]
190+
)
191+
192+
const showItem = useCallback(
193+
(note: NoteModel) => {
194+
const parents = findParentTreeItems(treeRef.current, note)
195+
setItemsExpandState(parents, true)
196+
},
197+
[setItemsExpandState]
198+
)
160199

161-
return paths
200+
const checkItemIsShown = useCallback((note: NoteModel) => {
201+
const parents = findParentTreeItems(treeRef.current, note)
202+
return reduce(parents, (value, item) => value && !!item.isExpanded, true)
162203
}, [])
163204

205+
const collapseAllItems = useCallback(() => {
206+
const expandedItems = TreeActions.flattenTree(treeRef.current).filter(
207+
(item) => item.isExpanded
208+
)
209+
setItemsExpandState(expandedItems, false)
210+
}, [setItemsExpandState])
211+
164212
const pinnedTree = useMemo(() => {
165213
const items = cloneDeep(tree.items)
166214
const pinnedIds: string[] = []
@@ -197,6 +245,9 @@ const useNoteTree = (initData: TreeModel = DEFAULT_TREE) => {
197245
restoreItem,
198246
deleteItem,
199247
getPaths,
248+
showItem,
249+
checkItemIsShown,
250+
collapseAllItems,
200251
loading,
201252
initLoaded,
202253
}

0 commit comments

Comments
 (0)