/
navigation.ts
117 lines (101 loc) · 3.3 KB
/
navigation.ts
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { NavItem, ParsedContentMeta } from '../types'
import { generateTitle } from './transformers/path-meta'
import { useRuntimeConfig } from '#imports'
type PrivateNavItem = NavItem & { path?: string }
/**
* Create NavItem array to be consumed from runtime plugin.
*/
export function createNav (contents: ParsedContentMeta[], configs: Record<string, ParsedContentMeta>) {
const { navigation } = useRuntimeConfig().content
const pickNavigationFields = pick(['title', ...navigation.fields])
const nav = contents
.sort((a, b) => a._path.localeCompare(b._path))
.reduce((nav, content) => {
const parts = content._path.substring(1).split('/')
const idParts = content._id.split(':').slice(1)
const isIndex = !!idParts[idParts.length - 1].match(/([1-9][0-9]*\.)?index.md/g)
const getNavItem = (content: ParsedContentMeta) => {
return {
title: content.title,
_path: content._path,
_file: content._file,
children: [],
...pickNavigationFields(content),
...(content._draft ? { _draft: true } : {})
}
}
const navItem: PrivateNavItem = getNavItem(content)
// Push index
if (isIndex) {
if (content._path !== '/') {
const indexItem = getNavItem(content)
navItem.children.push(indexItem)
}
Object.assign(
navItem,
pickNavigationFields(configs[navItem._path])
)
}
// First-level item, push it straight to nav
if (parts.length === 1) {
nav.push(navItem)
return nav
}
// Find siblings of current item and push them to parent children key
const siblings = parts.slice(0, -1).reduce((nodes, part, i) => {
// Part of current path
const currentPathPart = '/' + parts.slice(0, i + 1).join('/')
// Find parent node
let parent: PrivateNavItem = nodes.find(n => n._path === currentPathPart)
// Create dummy parent if not found
if (!parent) {
const conf = configs[currentPathPart]
parent = {
title: generateTitle(part),
_path: currentPathPart,
_file: content._file,
children: [],
...pickNavigationFields(conf)
}
nodes.push(parent)
}
return parent.children
}, nav)
siblings.push(navItem)
return nav
}, [] as PrivateNavItem[])
return sortAndClear(nav)
}
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })
/**
* Sort items by path and clear empty children keys.
*/
function sortAndClear (nav: PrivateNavItem[]) {
const sorted = nav.sort((a, b) => collator.compare(a._file, b._file))
for (const item of sorted) {
if (item.children.length) {
// Sort children
sortAndClear(item.children)
} else {
// Remove empty children
delete item.children
}
// Remove path after sort
delete item._file
}
return nav
}
/**
* Returns a new object with the specified keys
**/
function pick (keys?: string[]) {
return (obj: any) => {
obj = obj || {}
if (keys && keys.length) {
return keys
.filter(key => typeof obj[key] !== 'undefined')
.reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})
}
return obj
}
}