Skip to content

Commit ff310b2

Browse files
committed
feat: add custom navbar component with search box and styling adjustments
- Introduced a new VPNavbar component to enhance the navigation experience. - Implemented a search box with custom styles for better visibility and usability. - Adjusted the order of navbar items to prioritize the search box and color mode toggle. - Added SCSS styles to ensure proper layout and responsiveness of the navbar.
1 parent 45bc4b5 commit ff310b2

18 files changed

Lines changed: 3099 additions & 9182 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
node-version: [14.x]
14+
node-version: [20.x]
1515

1616
steps:
17+
- uses: actions/checkout@v4
18+
1719
- name: Use Node.js ${{ matrix.node-version }}
18-
uses: actions/setup-node@v2
20+
uses: actions/setup-node@v4
1921
with:
2022
node-version: ${{ matrix.node-version }}
21-
22-
- uses: actions/checkout@v2
23+
cache: 'yarn'
2324

2425
- name: Run Export
2526
run: |
@@ -28,7 +29,7 @@ jobs:
2829
yarn build
2930
3031
- name: Deploy Github-Pages
31-
uses: JamesIves/github-pages-deploy-action@4.1.4
32+
uses: JamesIves/github-pages-deploy-action@v4
3233
with:
3334
branch: gh-pages
3435
folder: .vuepress/dist

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ manifest
2626
.temp
2727
.obsidian
2828
.specstory/
29-
**/.DS_Store
29+
**/.DS_Store
30+
.cache/

.vuepress/components/GlobalTOC.vue

Lines changed: 107 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div>
3-
<div v-if="level===0" class="updateInfo not-print">
3+
<div v-if="level === 0" class="updateInfo not-print">
44
标记显示出
55
<select style="height: 23px;" v-model="updateDays">
66
<option value="0" selected>当天</option>
@@ -13,100 +13,133 @@
1313
内更新的内容
1414
</div>
1515
<ol>
16-
<li v-for="page in information">
16+
<li v-for="page in information" :key="page.links || page.title">
1717
<span v-if="page.links != null">
1818
<a :href="page.links">
19-
<span :class="'level'+level">{{page.title}}</span>
19+
<span :class="'level' + level">{{ page.title }}</span>
2020
</a>
2121
<div class="not-print" style="display: inline-block">
2222
<Badge type="error" v-if="checkUpdate(page)">
23-
{{page.update === 0 ? '当天更新': page.update+'天前更新'}}
23+
{{ page.update === 0 ? '当天更新' : page.update + '天前更新' }}
2424
</Badge>
2525
</div>
26-
<span class="words">{{page.words}}</span>
26+
<span class="words">{{ page.words }}</span>
2727
</span>
28-
<span v-else :class="'level'+level">
29-
{{page.title}}
30-
<span class="words">{{page.words}}</span>
28+
<span v-else :class="'level' + level">
29+
{{ page.title }}
30+
<span class="words">{{ page.words }}</span>
3131
</span>
32-
<GlobalTOC v-if="showDays === undefined" :pages="page.children" :level="level + 1" :showDays="updateDays"/>
33-
<GlobalTOC v-else :pages="page.children" :level="level + 1" :showDays="showDays"/>
32+
<GlobalTOC
33+
v-if="page.children && page.children.length"
34+
:pages="page.children"
35+
:level="level + 1"
36+
:showDays="showDays ?? updateDays"
37+
/>
3438
</li>
3539
</ol>
3640
</div>
3741
</template>
3842

3943
<script>
40-
import Badge from '@vuepress/theme-default/global-components/Badge'
41-
import moment from 'moment'
42-
43-
import {resolvePage} from '@parent-theme/util'
44-
45-
export default {
46-
name: "GlobalTOC",
47-
data() {
48-
return {
49-
updateDays: 0,
50-
items: [],
51-
information: []
52-
}
44+
import { computed, defineComponent, ref } from 'vue'
45+
import { usePagesData, useThemeData } from '@vuepress/client'
46+
import Badge from '@vuepress/theme-default/lib/client/components/Badge.vue'
47+
import moment from 'moment'
48+
49+
const normalizePath = (value = '') => value.replace(/\/$/, '')
50+
51+
const resolvePage = (pages, link) => {
52+
if (!link) return null
53+
const target = normalizePath(link)
54+
return pages.find((page) => normalizePath(page.path) === target) || null
55+
}
56+
57+
export default defineComponent({
58+
name: 'GlobalTOC',
59+
components: {
60+
Badge
61+
},
62+
props: {
63+
pages: {
64+
type: [Array, String],
65+
default: null
5366
},
54-
props: ['pages', 'level', 'showDays'],
55-
created: function () {
56-
if (this.pages) {
57-
let origin = (this.pages === '/' ? this.$themeConfig.sidebar : this.pages);
58-
this.items = origin.map(item => {
59-
let page
60-
if (item.path) {
61-
page = resolvePage(this.$site.pages, item.path, this.$route.path)
62-
} else if (typeof (item) === 'string') {
63-
page = resolvePage(this.$site.pages, item, this.$route.path)
64-
} else {
65-
page = item;
66-
}
67-
page.children = item.children
68-
return page;
69-
})
70-
this.information = this.items.map(item => {
71-
return {
72-
title: this.getTitle(item),
73-
words: this.getWords(item),
74-
links: this.getLinks(item),
75-
update: this.getUpdate(item),
76-
lastUpdated: item.lastUpdated,
77-
children: item.children
78-
}
79-
})
80-
}
67+
level: {
68+
type: Number,
69+
default: 0
8170
},
82-
methods: {
83-
checkUpdate: function (page) {
84-
return page.update <= Math.max(this.updateDays, this.showDays);
85-
},
86-
getTitle: function (page) {
87-
try {
88-
return page.title.replace('✔️ ', '')
89-
} catch (e) {
90-
return "标题错误"
91-
}
92-
},
93-
getWords: function (page) {
94-
if (page && page.readingTime) {
95-
return `${page.readingTime.words.toLocaleString()} 字 `
96-
} else {
97-
return ""
71+
showDays: {
72+
type: Number,
73+
default: undefined
74+
}
75+
},
76+
setup(props) {
77+
const updateDays = ref(0)
78+
const pagesData = usePagesData()
79+
const themeData = useThemeData()
80+
81+
const items = computed(() => {
82+
const origin = props.pages
83+
? props.pages === '/'
84+
? themeData.value.sidebar || []
85+
: props.pages
86+
: themeData.value.sidebar || []
87+
88+
return origin.map((item) => {
89+
const normalizedItem = typeof item === 'string' ? { link: item } : item
90+
const page = normalizedItem.link
91+
? resolvePage(pagesData.value, normalizedItem.link)
92+
: normalizedItem
93+
return {
94+
...(page || {}),
95+
children: normalizedItem.children || []
9896
}
99-
},
100-
getLinks: function (page) {
101-
//return (page.readingTime && page.readingTime.words > 100) ? "/TechShare" + page.path : null
102-
return (page.readingTime && page.readingTime.words > 100) ? page.path : null
103-
},
104-
getUpdate: function (page) {
105-
let lastDay = new moment(page.lastUpdated, 'L');
106-
return Math.floor(-1 * moment.duration(lastDay.diff(new Date())).asDays())
97+
})
98+
})
99+
100+
const getTitle = (page) => {
101+
try {
102+
return page.title?.replace('✔️ ', '') || '标题错误'
103+
} catch (error) {
104+
return '标题错误'
107105
}
108106
}
107+
108+
const getWords = (page) => {
109+
if (page?.readingTime?.words) {
110+
return `${page.readingTime.words.toLocaleString()} 字 `
111+
}
112+
return ''
113+
}
114+
115+
const getLinks = (page) => (page?.readingTime?.words > 100 ? page.path : null)
116+
117+
const getUpdate = (page) => {
118+
if (!page?.lastUpdated) return Number.POSITIVE_INFINITY
119+
const lastDay = new moment(page.lastUpdated, 'L')
120+
return Math.floor(-1 * moment.duration(lastDay.diff(new Date())).asDays())
121+
}
122+
123+
const information = computed(() =>
124+
items.value.map((item) => ({
125+
title: getTitle(item),
126+
words: getWords(item),
127+
links: getLinks(item),
128+
update: getUpdate(item),
129+
lastUpdated: item.lastUpdated,
130+
children: item.children || []
131+
}))
132+
)
133+
134+
const checkUpdate = (page) => page.update <= Math.max(updateDays.value, props.showDays ?? 0)
135+
136+
return {
137+
updateDays,
138+
information,
139+
checkUpdate
140+
}
109141
}
142+
})
110143
</script>
111144
112145
<style scoped>
Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,60 @@
1-
<script>
2-
export default {
3-
name: 'github-button',
4-
props: ['href', 'ariaLabel', 'title', 'dataIcon', 'dataColorScheme', 'dataSize', 'dataShowCount', 'dataText'],
5-
render: function (h) {
6-
return h('span', [
7-
h('a', {
8-
attrs: {
9-
'href': this.href,
10-
'aria-label': this.ariaLabel,
11-
'title': this.title,
12-
'data-icon': this.dataIcon,
13-
'data-color-scheme': this.dataColorScheme,
14-
'data-size': this.dataSize,
15-
'data-show-count': this.dataShowCount,
16-
'data-text': this.dataText
17-
},
18-
ref: '_'
19-
}, this.$slots.default)
20-
])
21-
},
22-
mounted: function () {
23-
this.paint()
24-
},
25-
beforeUpdate: function () {
26-
this.reset()
27-
},
28-
updated: function () {
29-
this.paint()
30-
},
31-
beforeDestroy: function () {
32-
this.reset()
33-
},
34-
methods: {
35-
paint: function () {
36-
const _ = this.$el.appendChild(document.createElement('span'))
37-
const _this = this
38-
import(/* webpackMode: "eager" */ 'github-buttons').then(function (module) {
39-
module.render(_.appendChild(_this.$refs._), function (el) {
40-
try {
41-
_.parentNode.replaceChild(el, _)
42-
} catch (_) {
43-
}
44-
})
45-
})
46-
},
47-
reset: function () {
48-
this.$el.replaceChild(this.$refs._, this.$el.lastChild)
49-
}
50-
}
1+
<template>
2+
<span ref="root">
3+
<a
4+
ref="link"
5+
:href="href"
6+
:aria-label="ariaLabel"
7+
:title="title"
8+
:data-icon="dataIcon"
9+
:data-color-scheme="dataColorScheme"
10+
:data-size="dataSize"
11+
:data-show-count="dataShowCount"
12+
:data-text="dataText"
13+
>
14+
<slot />
15+
</a>
16+
</span>
17+
</template>
18+
19+
<script setup>
20+
import { onBeforeUnmount, onBeforeUpdate, onMounted, onUpdated, ref } from 'vue'
21+
22+
const props = defineProps({
23+
href: String,
24+
ariaLabel: String,
25+
title: String,
26+
dataIcon: String,
27+
dataColorScheme: String,
28+
dataSize: String,
29+
dataShowCount: String,
30+
dataText: String
31+
})
32+
33+
const root = ref(null)
34+
const link = ref(null)
35+
36+
const paint = async () => {
37+
if (!root.value || !link.value) return
38+
const placeholder = document.createElement('span')
39+
root.value.appendChild(placeholder)
40+
41+
const module = await import('github-buttons')
42+
module.render(placeholder.appendChild(link.value), (el) => {
43+
try {
44+
placeholder.parentNode.replaceChild(el, placeholder)
45+
} catch (error) {
46+
console.error(error)
5147
}
48+
})
49+
}
50+
51+
const reset = () => {
52+
if (!root.value || !link.value || !root.value.lastChild) return
53+
root.value.replaceChild(link.value, root.value.lastChild)
54+
}
55+
56+
onMounted(paint)
57+
onBeforeUpdate(reset)
58+
onUpdated(paint)
59+
onBeforeUnmount(reset)
5260
</script>

0 commit comments

Comments
 (0)