vitepress-theme-element-plus 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/client/components/A11yTag.vue +29 -29
- package/client/components/ApiTyping.vue +54 -54
- package/client/components/Backdrop.vue +41 -41
- package/client/components/Bili.vue +94 -94
- package/client/components/Content.vue +148 -150
- package/client/components/DeprecatedTag.vue +19 -19
- package/client/components/Doc.vue +181 -181
- package/client/components/DocAside.vue +46 -46
- package/client/components/DocAsideOutline.vue +82 -82
- package/client/components/DocFooter.vue +159 -159
- package/client/components/Footer.vue +77 -77
- package/client/components/FooterCopyright.vue +27 -27
- package/client/components/Layout.vue +156 -156
- package/client/components/Link.vue +41 -41
- package/client/components/LocalNav.vue +160 -160
- package/client/components/Nav.vue +69 -69
- package/client/components/NavBar.vue +203 -203
- package/client/components/NavBarTitle.vue +75 -75
- package/client/components/Sidebar.vue +129 -129
- package/client/components/SidebarGroup.vue +51 -51
- package/client/components/SidebarItem.vue +302 -302
- package/client/components/Tag.vue +25 -25
- package/client/components/VPNavBarSearch.vue +23 -23
- package/client/components/VersionTag.vue +18 -18
- package/client/hooks/useBackTop.ts +71 -71
- package/client/hooks/useLangs.ts +50 -50
- package/client/hooks/useSidebar.ts +93 -18
- package/client/hooks/useSidebarControl.ts +78 -78
- package/client/hooks/useSize.ts +69 -69
- package/client/utils/client/common.ts +49 -49
- package/client/utils/client/outline.ts +113 -113
- package/client/utils/common.ts +90 -90
- package/index.ts +26 -26
- package/package.json +73 -73
- package/shared/constants.ts +3 -3
- package/styles/base.scss +37 -37
- package/styles/code.scss +282 -282
- package/styles/doc-content.scss +161 -161
- package/styles/index.scss +69 -69
- package/styles/tag-content.scss +30 -30
package/client/hooks/useSize.ts
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
import type { MaybeRef } from '@vueuse/core'
|
|
2
|
-
import type { Ref, ShallowRef } from 'vue'
|
|
3
|
-
import { useEventListener } from '@vueuse/core'
|
|
4
|
-
import { computed, isRef, onMounted, ref, shallowRef, unref, watch } from 'vue'
|
|
5
|
-
import { isString } from '../utils/common'
|
|
6
|
-
|
|
7
|
-
function getValue(value: string | number): string {
|
|
8
|
-
return isString(value) ? value : `${value}px`
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface SizeOptions {
|
|
12
|
-
width: string | number | undefined
|
|
13
|
-
height: string | number | undefined
|
|
14
|
-
ratio: string | number | undefined
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface SizeInfo<E extends HTMLElement> {
|
|
18
|
-
el: ShallowRef<E | undefined>
|
|
19
|
-
width: Ref<string>
|
|
20
|
-
height: Ref<string>
|
|
21
|
-
resize: () => void
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function useSize<E extends HTMLElement>(options: SizeOptions, extraHeight: MaybeRef<number> = 0): SizeInfo<E> {
|
|
25
|
-
const el = shallowRef<E>()
|
|
26
|
-
const width = computed(() => getValue(unref(options.width) ?? '100%'))
|
|
27
|
-
const height = ref('auto')
|
|
28
|
-
|
|
29
|
-
const getRadio = (ratio: number | string | undefined): number => {
|
|
30
|
-
if (isString(ratio)) {
|
|
31
|
-
const [width, height] = ratio.split(':')
|
|
32
|
-
const parsedRadio = Number(width) / Number(height)
|
|
33
|
-
|
|
34
|
-
if (!Number.isNaN(parsedRadio))
|
|
35
|
-
return parsedRadio
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return typeof ratio === 'number' ? ratio : 16 / 9
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const getHeight = (width: number): string => {
|
|
42
|
-
const height = unref(options.height)
|
|
43
|
-
const ratio = getRadio(unref(options.ratio))
|
|
44
|
-
|
|
45
|
-
return height
|
|
46
|
-
? getValue(height)
|
|
47
|
-
: `${Number(width) / ratio + unref(extraHeight)}px`
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const updateHeight = (): void => {
|
|
51
|
-
if (el.value)
|
|
52
|
-
height.value = getHeight(el.value.clientWidth)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
onMounted(() => {
|
|
56
|
-
updateHeight()
|
|
57
|
-
if (isRef(extraHeight))
|
|
58
|
-
watch(extraHeight, updateHeight)
|
|
59
|
-
useEventListener('orientationchange', updateHeight)
|
|
60
|
-
useEventListener('resize', updateHeight)
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
el,
|
|
65
|
-
width,
|
|
66
|
-
height,
|
|
67
|
-
resize: updateHeight,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
1
|
+
import type { MaybeRef } from '@vueuse/core'
|
|
2
|
+
import type { Ref, ShallowRef } from 'vue'
|
|
3
|
+
import { useEventListener } from '@vueuse/core'
|
|
4
|
+
import { computed, isRef, onMounted, ref, shallowRef, unref, watch } from 'vue'
|
|
5
|
+
import { isString } from '../utils/common'
|
|
6
|
+
|
|
7
|
+
function getValue(value: string | number): string {
|
|
8
|
+
return isString(value) ? value : `${value}px`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SizeOptions {
|
|
12
|
+
width: string | number | undefined
|
|
13
|
+
height: string | number | undefined
|
|
14
|
+
ratio: string | number | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SizeInfo<E extends HTMLElement> {
|
|
18
|
+
el: ShallowRef<E | undefined>
|
|
19
|
+
width: Ref<string>
|
|
20
|
+
height: Ref<string>
|
|
21
|
+
resize: () => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useSize<E extends HTMLElement>(options: SizeOptions, extraHeight: MaybeRef<number> = 0): SizeInfo<E> {
|
|
25
|
+
const el = shallowRef<E>()
|
|
26
|
+
const width = computed(() => getValue(unref(options.width) ?? '100%'))
|
|
27
|
+
const height = ref('auto')
|
|
28
|
+
|
|
29
|
+
const getRadio = (ratio: number | string | undefined): number => {
|
|
30
|
+
if (isString(ratio)) {
|
|
31
|
+
const [width, height] = ratio.split(':')
|
|
32
|
+
const parsedRadio = Number(width) / Number(height)
|
|
33
|
+
|
|
34
|
+
if (!Number.isNaN(parsedRadio))
|
|
35
|
+
return parsedRadio
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return typeof ratio === 'number' ? ratio : 16 / 9
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const getHeight = (width: number): string => {
|
|
42
|
+
const height = unref(options.height)
|
|
43
|
+
const ratio = getRadio(unref(options.ratio))
|
|
44
|
+
|
|
45
|
+
return height
|
|
46
|
+
? getValue(height)
|
|
47
|
+
: `${Number(width) / ratio + unref(extraHeight)}px`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const updateHeight = (): void => {
|
|
51
|
+
if (el.value)
|
|
52
|
+
height.value = getHeight(el.value.clientWidth)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onMounted(() => {
|
|
56
|
+
updateHeight()
|
|
57
|
+
if (isRef(extraHeight))
|
|
58
|
+
watch(extraHeight, updateHeight)
|
|
59
|
+
useEventListener('orientationchange', updateHeight)
|
|
60
|
+
useEventListener('resize', updateHeight)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
el,
|
|
65
|
+
width,
|
|
66
|
+
height,
|
|
67
|
+
resize: updateHeight,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import type { DefaultTheme } from 'vitepress'
|
|
2
|
-
import { isActive } from '../common'
|
|
3
|
-
|
|
4
|
-
export interface SidebarLink {
|
|
5
|
-
text: string
|
|
6
|
-
link: string
|
|
7
|
-
docFooterText?: string
|
|
8
|
-
}
|
|
9
|
-
export function getFlatSideBarLinks(sidebar: DefaultTheme.SidebarItem[]): SidebarLink[] {
|
|
10
|
-
const links: SidebarLink[] = []
|
|
11
|
-
|
|
12
|
-
function recursivelyExtractLinks(items: DefaultTheme.SidebarItem[]) {
|
|
13
|
-
for (const item of items) {
|
|
14
|
-
if (item.text && item.link) {
|
|
15
|
-
links.push({
|
|
16
|
-
text: item.text,
|
|
17
|
-
link: item.link,
|
|
18
|
-
docFooterText: item.docFooterText,
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (item.items) {
|
|
23
|
-
recursivelyExtractLinks(item.items)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
recursivelyExtractLinks(sidebar)
|
|
29
|
-
|
|
30
|
-
return links
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Check if the given sidebar item contains any active link.
|
|
35
|
-
*/
|
|
36
|
-
export function hasActiveLink(
|
|
37
|
-
path: string,
|
|
38
|
-
items: DefaultTheme.SidebarItem | DefaultTheme.SidebarItem[],
|
|
39
|
-
): boolean {
|
|
40
|
-
if (Array.isArray(items)) {
|
|
41
|
-
return items.some(item => hasActiveLink(path, item))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return isActive(path, items.link)
|
|
45
|
-
? true
|
|
46
|
-
: items.items
|
|
47
|
-
? hasActiveLink(path, items.items)
|
|
48
|
-
: false
|
|
49
|
-
}
|
|
1
|
+
import type { DefaultTheme } from 'vitepress'
|
|
2
|
+
import { isActive } from '../common'
|
|
3
|
+
|
|
4
|
+
export interface SidebarLink {
|
|
5
|
+
text: string
|
|
6
|
+
link: string
|
|
7
|
+
docFooterText?: string
|
|
8
|
+
}
|
|
9
|
+
export function getFlatSideBarLinks(sidebar: DefaultTheme.SidebarItem[]): SidebarLink[] {
|
|
10
|
+
const links: SidebarLink[] = []
|
|
11
|
+
|
|
12
|
+
function recursivelyExtractLinks(items: DefaultTheme.SidebarItem[]) {
|
|
13
|
+
for (const item of items) {
|
|
14
|
+
if (item.text && item.link) {
|
|
15
|
+
links.push({
|
|
16
|
+
text: item.text,
|
|
17
|
+
link: item.link,
|
|
18
|
+
docFooterText: item.docFooterText,
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (item.items) {
|
|
23
|
+
recursivelyExtractLinks(item.items)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
recursivelyExtractLinks(sidebar)
|
|
29
|
+
|
|
30
|
+
return links
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check if the given sidebar item contains any active link.
|
|
35
|
+
*/
|
|
36
|
+
export function hasActiveLink(
|
|
37
|
+
path: string,
|
|
38
|
+
items: DefaultTheme.SidebarItem | DefaultTheme.SidebarItem[],
|
|
39
|
+
): boolean {
|
|
40
|
+
if (Array.isArray(items)) {
|
|
41
|
+
return items.some(item => hasActiveLink(path, item))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return isActive(path, items.link)
|
|
45
|
+
? true
|
|
46
|
+
: items.items
|
|
47
|
+
? hasActiveLink(path, items.items)
|
|
48
|
+
: false
|
|
49
|
+
}
|
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
import type { Header } from 'vitepress'
|
|
2
|
-
import type { DefaultTheme } from 'vitepress/theme'
|
|
3
|
-
|
|
4
|
-
const ignoreRE = /\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/
|
|
5
|
-
|
|
6
|
-
// cached list of anchor elements from resolveHeaders
|
|
7
|
-
const resolvedHeaders: { element: HTMLHeadElement, link: string }[] = []
|
|
8
|
-
|
|
9
|
-
export type MenuItem = Omit<Header, 'slug' | 'children'> & {
|
|
10
|
-
element: HTMLHeadElement
|
|
11
|
-
children?: MenuItem[]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function resolveTitle(theme: DefaultTheme.Config): string {
|
|
15
|
-
return (
|
|
16
|
-
(typeof theme.outline === 'object'
|
|
17
|
-
&& !Array.isArray(theme.outline)
|
|
18
|
-
&& theme.outline.label)
|
|
19
|
-
|| theme.outlineTitle
|
|
20
|
-
|| 'On this page'
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function getHeaders(range: DefaultTheme.Config['outline']): MenuItem[] {
|
|
25
|
-
const headers = Array.from(document.querySelectorAll('.VPDoc :where(h1,h2,h3,h4,h5,h6)'))
|
|
26
|
-
.filter(el => el.id && el.hasChildNodes())
|
|
27
|
-
.map((el) => {
|
|
28
|
-
const level = Number(el.tagName[1])
|
|
29
|
-
return {
|
|
30
|
-
element: el as HTMLHeadElement,
|
|
31
|
-
title: serializeHeader(el),
|
|
32
|
-
link: `#${el.id}`,
|
|
33
|
-
level,
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
return resolveHeaders(headers, range)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function serializeHeader(h: Element): string {
|
|
41
|
-
let ret = ''
|
|
42
|
-
for (const node of Array.from(h.childNodes)) {
|
|
43
|
-
if (node.nodeType === 1) {
|
|
44
|
-
if (ignoreRE.test((node as Element).className))
|
|
45
|
-
continue
|
|
46
|
-
ret += node.textContent
|
|
47
|
-
}
|
|
48
|
-
else if (node.nodeType === 3) {
|
|
49
|
-
ret += node.textContent
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return ret.trim()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function resolveHeaders(
|
|
56
|
-
headers: MenuItem[],
|
|
57
|
-
range?: DefaultTheme.Config['outline'],
|
|
58
|
-
): MenuItem[] {
|
|
59
|
-
if (range === false) {
|
|
60
|
-
return []
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const levelsRange
|
|
64
|
-
= (typeof range === 'object' && !Array.isArray(range)
|
|
65
|
-
? range.level
|
|
66
|
-
: range) || 2
|
|
67
|
-
|
|
68
|
-
const [high, low]: [number, number]
|
|
69
|
-
= typeof levelsRange === 'number'
|
|
70
|
-
? [levelsRange, levelsRange]
|
|
71
|
-
: levelsRange === 'deep'
|
|
72
|
-
? [2, 6]
|
|
73
|
-
: levelsRange
|
|
74
|
-
|
|
75
|
-
return buildTree(headers, high, low)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function buildTree(data: MenuItem[], min: number, max: number): MenuItem[] {
|
|
79
|
-
resolvedHeaders.length = 0
|
|
80
|
-
|
|
81
|
-
const result: MenuItem[] = []
|
|
82
|
-
const stack: (MenuItem | { level: number, shouldIgnore: true })[] = []
|
|
83
|
-
|
|
84
|
-
data.forEach((item) => {
|
|
85
|
-
const node = { ...item, children: [] }
|
|
86
|
-
let parent = stack[stack.length - 1]
|
|
87
|
-
|
|
88
|
-
while (parent && parent.level >= node.level) {
|
|
89
|
-
stack.pop()
|
|
90
|
-
parent = stack[stack.length - 1]
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
node.element.classList.contains('ignore-header')
|
|
95
|
-
|| (parent && 'shouldIgnore' in parent)
|
|
96
|
-
) {
|
|
97
|
-
stack.push({ level: node.level, shouldIgnore: true })
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (node.level > max || node.level < min)
|
|
102
|
-
return
|
|
103
|
-
resolvedHeaders.push({ element: node.element, link: node.link })
|
|
104
|
-
|
|
105
|
-
if (parent)
|
|
106
|
-
(parent as MenuItem).children.push(node)
|
|
107
|
-
else result.push(node)
|
|
108
|
-
|
|
109
|
-
stack.push(node)
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
return result
|
|
113
|
-
}
|
|
1
|
+
import type { Header } from 'vitepress'
|
|
2
|
+
import type { DefaultTheme } from 'vitepress/theme'
|
|
3
|
+
|
|
4
|
+
const ignoreRE = /\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/
|
|
5
|
+
|
|
6
|
+
// cached list of anchor elements from resolveHeaders
|
|
7
|
+
const resolvedHeaders: { element: HTMLHeadElement, link: string }[] = []
|
|
8
|
+
|
|
9
|
+
export type MenuItem = Omit<Header, 'slug' | 'children'> & {
|
|
10
|
+
element: HTMLHeadElement
|
|
11
|
+
children?: MenuItem[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function resolveTitle(theme: DefaultTheme.Config): string {
|
|
15
|
+
return (
|
|
16
|
+
(typeof theme.outline === 'object'
|
|
17
|
+
&& !Array.isArray(theme.outline)
|
|
18
|
+
&& theme.outline.label)
|
|
19
|
+
|| theme.outlineTitle
|
|
20
|
+
|| 'On this page'
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getHeaders(range: DefaultTheme.Config['outline']): MenuItem[] {
|
|
25
|
+
const headers = Array.from(document.querySelectorAll('.VPDoc :where(h1,h2,h3,h4,h5,h6)'))
|
|
26
|
+
.filter(el => el.id && el.hasChildNodes())
|
|
27
|
+
.map((el) => {
|
|
28
|
+
const level = Number(el.tagName[1])
|
|
29
|
+
return {
|
|
30
|
+
element: el as HTMLHeadElement,
|
|
31
|
+
title: serializeHeader(el),
|
|
32
|
+
link: `#${el.id}`,
|
|
33
|
+
level,
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
return resolveHeaders(headers, range)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function serializeHeader(h: Element): string {
|
|
41
|
+
let ret = ''
|
|
42
|
+
for (const node of Array.from(h.childNodes)) {
|
|
43
|
+
if (node.nodeType === 1) {
|
|
44
|
+
if (ignoreRE.test((node as Element).className))
|
|
45
|
+
continue
|
|
46
|
+
ret += node.textContent
|
|
47
|
+
}
|
|
48
|
+
else if (node.nodeType === 3) {
|
|
49
|
+
ret += node.textContent
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return ret.trim()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function resolveHeaders(
|
|
56
|
+
headers: MenuItem[],
|
|
57
|
+
range?: DefaultTheme.Config['outline'],
|
|
58
|
+
): MenuItem[] {
|
|
59
|
+
if (range === false) {
|
|
60
|
+
return []
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const levelsRange
|
|
64
|
+
= (typeof range === 'object' && !Array.isArray(range)
|
|
65
|
+
? range.level
|
|
66
|
+
: range) || 2
|
|
67
|
+
|
|
68
|
+
const [high, low]: [number, number]
|
|
69
|
+
= typeof levelsRange === 'number'
|
|
70
|
+
? [levelsRange, levelsRange]
|
|
71
|
+
: levelsRange === 'deep'
|
|
72
|
+
? [2, 6]
|
|
73
|
+
: levelsRange
|
|
74
|
+
|
|
75
|
+
return buildTree(headers, high, low)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildTree(data: MenuItem[], min: number, max: number): MenuItem[] {
|
|
79
|
+
resolvedHeaders.length = 0
|
|
80
|
+
|
|
81
|
+
const result: MenuItem[] = []
|
|
82
|
+
const stack: (MenuItem | { level: number, shouldIgnore: true })[] = []
|
|
83
|
+
|
|
84
|
+
data.forEach((item) => {
|
|
85
|
+
const node = { ...item, children: [] }
|
|
86
|
+
let parent = stack[stack.length - 1]
|
|
87
|
+
|
|
88
|
+
while (parent && parent.level >= node.level) {
|
|
89
|
+
stack.pop()
|
|
90
|
+
parent = stack[stack.length - 1]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
node.element.classList.contains('ignore-header')
|
|
95
|
+
|| (parent && 'shouldIgnore' in parent)
|
|
96
|
+
) {
|
|
97
|
+
stack.push({ level: node.level, shouldIgnore: true })
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (node.level > max || node.level < min)
|
|
102
|
+
return
|
|
103
|
+
resolvedHeaders.push({ element: node.element, link: node.link })
|
|
104
|
+
|
|
105
|
+
if (parent)
|
|
106
|
+
(parent as MenuItem).children.push(node)
|
|
107
|
+
else result.push(node)
|
|
108
|
+
|
|
109
|
+
stack.push(node)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return result
|
|
113
|
+
}
|
package/client/utils/common.ts
CHANGED
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
const HASH_RE = /#.*$/
|
|
2
|
-
const HASH_OR_QUERY_RE = /[?#].*$/
|
|
3
|
-
const INDEX_OR_EXT_RE = /(?:(^|\/)index)?\.(?:md|html)$/
|
|
4
|
-
|
|
5
|
-
export const inBrowser = typeof document !== 'undefined'
|
|
6
|
-
|
|
7
|
-
export function isActive(
|
|
8
|
-
currentPath: string,
|
|
9
|
-
matchPath?: string,
|
|
10
|
-
asRegex: boolean = false,
|
|
11
|
-
): boolean {
|
|
12
|
-
if (matchPath === undefined) {
|
|
13
|
-
return false
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
currentPath = normalize(`/${currentPath}`)
|
|
17
|
-
|
|
18
|
-
if (asRegex) {
|
|
19
|
-
return new RegExp(matchPath).test(currentPath)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (normalize(matchPath) !== currentPath) {
|
|
23
|
-
return false
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const hashMatch = matchPath.match(HASH_RE)
|
|
27
|
-
|
|
28
|
-
if (hashMatch) {
|
|
29
|
-
return (inBrowser ? location.hash : '') === hashMatch[0]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return true
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function normalize(path: string): string {
|
|
36
|
-
return decodeURI(path)
|
|
37
|
-
.replace(HASH_OR_QUERY_RE, '')
|
|
38
|
-
.replace(INDEX_OR_EXT_RE, '$1')
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function ensureStartingSlash(path: string): string {
|
|
42
|
-
return /^\//.test(path) ? path : `/${path}`
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function isPlainObject(obj: any) {
|
|
46
|
-
// 首先排除 null 和非对象类型
|
|
47
|
-
if (obj === null || typeof obj !== 'object') {
|
|
48
|
-
return false
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 检查对象的构造函数是否是 Object
|
|
52
|
-
if (obj.constructor !== Object) {
|
|
53
|
-
return false
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// 检查对象的原型是否是 Object.prototype
|
|
57
|
-
if (Object.getPrototypeOf(obj) !== Object.prototype) {
|
|
58
|
-
return false
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return true
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function isString(value) {
|
|
65
|
-
return typeof value === 'string'
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function capitalizeFirstLetter(string: string) {
|
|
69
|
-
if (!string)
|
|
70
|
-
return '' // 如果字符串为空,返回空字符串
|
|
71
|
-
return string.charAt(0).toUpperCase() + string.slice(1)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function throttleAndDebounce(fn: () => void, delay: number): () => void {
|
|
75
|
-
let timeoutId: NodeJS.Timeout
|
|
76
|
-
let called = false
|
|
77
|
-
|
|
78
|
-
return () => {
|
|
79
|
-
if (timeoutId)
|
|
80
|
-
clearTimeout(timeoutId)
|
|
81
|
-
|
|
82
|
-
if (!called) {
|
|
83
|
-
fn()
|
|
84
|
-
;(called = true) && setTimeout(() => (called = false), delay)
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
timeoutId = setTimeout(fn, delay)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
1
|
+
const HASH_RE = /#.*$/
|
|
2
|
+
const HASH_OR_QUERY_RE = /[?#].*$/
|
|
3
|
+
const INDEX_OR_EXT_RE = /(?:(^|\/)index)?\.(?:md|html)$/
|
|
4
|
+
|
|
5
|
+
export const inBrowser = typeof document !== 'undefined'
|
|
6
|
+
|
|
7
|
+
export function isActive(
|
|
8
|
+
currentPath: string,
|
|
9
|
+
matchPath?: string,
|
|
10
|
+
asRegex: boolean = false,
|
|
11
|
+
): boolean {
|
|
12
|
+
if (matchPath === undefined) {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
currentPath = normalize(`/${currentPath}`)
|
|
17
|
+
|
|
18
|
+
if (asRegex) {
|
|
19
|
+
return new RegExp(matchPath).test(currentPath)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (normalize(matchPath) !== currentPath) {
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const hashMatch = matchPath.match(HASH_RE)
|
|
27
|
+
|
|
28
|
+
if (hashMatch) {
|
|
29
|
+
return (inBrowser ? location.hash : '') === hashMatch[0]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalize(path: string): string {
|
|
36
|
+
return decodeURI(path)
|
|
37
|
+
.replace(HASH_OR_QUERY_RE, '')
|
|
38
|
+
.replace(INDEX_OR_EXT_RE, '$1')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function ensureStartingSlash(path: string): string {
|
|
42
|
+
return /^\//.test(path) ? path : `/${path}`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function isPlainObject(obj: any) {
|
|
46
|
+
// 首先排除 null 和非对象类型
|
|
47
|
+
if (obj === null || typeof obj !== 'object') {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 检查对象的构造函数是否是 Object
|
|
52
|
+
if (obj.constructor !== Object) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 检查对象的原型是否是 Object.prototype
|
|
57
|
+
if (Object.getPrototypeOf(obj) !== Object.prototype) {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isString(value) {
|
|
65
|
+
return typeof value === 'string'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function capitalizeFirstLetter(string: string) {
|
|
69
|
+
if (!string)
|
|
70
|
+
return '' // 如果字符串为空,返回空字符串
|
|
71
|
+
return string.charAt(0).toUpperCase() + string.slice(1)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function throttleAndDebounce(fn: () => void, delay: number): () => void {
|
|
75
|
+
let timeoutId: NodeJS.Timeout
|
|
76
|
+
let called = false
|
|
77
|
+
|
|
78
|
+
return () => {
|
|
79
|
+
if (timeoutId)
|
|
80
|
+
clearTimeout(timeoutId)
|
|
81
|
+
|
|
82
|
+
if (!called) {
|
|
83
|
+
fn()
|
|
84
|
+
;(called = true) && setTimeout(() => (called = false), delay)
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
timeoutId = setTimeout(fn, delay)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|