Skip to content

Commit b5ecc13

Browse files
committed
release: v4.0.0-beta.2
1 parent c6f9161 commit b5ecc13

20 files changed

+2042
-1
lines changed

Diff for: __old/SidebarChild.ts

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { h } from 'vue'
2+
import type { FunctionalComponent, VNode } from 'vue'
3+
import { RouterLink, useRoute } from 'vue-router'
4+
import type { RouteLocationNormalizedLoaded } from 'vue-router'
5+
import type { ResolvedSidebarItem } from '../../shared'
6+
7+
import { CNavGroup, CNavItem } from './../../../../../../src'
8+
import { CIcon } from '@coreui/icons-vue'
9+
10+
const normalizePath = (path: string): string =>
11+
decodeURI(path)
12+
.replace(/#.*$/, '')
13+
.replace(/(index)?\.(md|html)$/, '')
14+
15+
const isActiveLink = (route: RouteLocationNormalizedLoaded, link?: string): boolean => {
16+
if (link === undefined) {
17+
return false
18+
}
19+
20+
if (route.hash === link) {
21+
return true
22+
}
23+
24+
const currentPath = normalizePath(route.path)
25+
const targetPath = normalizePath(link)
26+
27+
return currentPath === targetPath
28+
}
29+
30+
const isActiveItem = (route: RouteLocationNormalizedLoaded, item: ResolvedSidebarItem): boolean => {
31+
if (isActiveLink(route, item.link)) {
32+
return true
33+
}
34+
35+
if (item.children) {
36+
return item.children.some((child) => isActiveItem(route, child))
37+
}
38+
39+
return false
40+
}
41+
42+
const renderItem = (item: ResolvedSidebarItem): VNode => {
43+
const route = useRoute()
44+
if (item.children && !item.link.includes('.html')) {
45+
return h(
46+
CNavGroup,
47+
{
48+
compact: true,
49+
visible: item.children.some((child) => isActiveItem(route, child)),
50+
// href: props.href,
51+
},
52+
{
53+
togglerContent: () => [
54+
h(CIcon, {
55+
customClassName: 'nav-icon text-primary',
56+
icon: ['512 512', item.icon],
57+
height: 64,
58+
width: 64,
59+
}),
60+
item.text,
61+
],
62+
default: () => item.children.map((child) => renderItem(child)),
63+
},
64+
)
65+
}
66+
67+
return h(
68+
RouterLink,
69+
{
70+
to: item.link,
71+
custom: true,
72+
},
73+
{
74+
default: (props) =>
75+
h(
76+
CNavItem,
77+
{
78+
active: props.isActive,
79+
href: item.link,
80+
},
81+
{
82+
default: () => item.text,
83+
},
84+
),
85+
},
86+
)
87+
}
88+
89+
export const SidebarChild: FunctionalComponent<{
90+
item: ResolvedSidebarItem
91+
}> = ({ item }) => renderItem(item)
92+
93+
SidebarChild.displayName = 'SidebarChild'
94+
95+
SidebarChild.props = {
96+
item: {
97+
type: Object,
98+
required: true,
99+
},
100+
}

Diff for: __old/_DropdownLink.vue

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<template>
2+
<div class="dropdown-wrapper" :class="{ open }">
3+
<button
4+
class="dropdown-title"
5+
type="button"
6+
:aria-label="dropdownAriaLabel"
7+
@click="handleDropdown"
8+
>
9+
<span class="title">{{ item.text }}</span>
10+
<span class="arrow down" />
11+
</button>
12+
13+
<button
14+
class="mobile-dropdown-title"
15+
type="button"
16+
:aria-label="dropdownAriaLabel"
17+
@click="open = !open"
18+
>
19+
<span class="title">{{ item.text }}</span>
20+
<span class="arrow" :class="open ? 'down' : 'right'" />
21+
</button>
22+
23+
<!-- Transition will somehow break the Select Languages dropdown -->
24+
<!-- So we remove DropdownTransition until we figure out the reason -->
25+
<ul v-show="open" class="nav-dropdown">
26+
<li
27+
v-for="(child, index) in item.children"
28+
:key="child.link || index"
29+
class="dropdown-item"
30+
>
31+
<template v-if="child.children">
32+
<h4 class="dropdown-subtitle">
33+
<NavLink
34+
v-if="child.link"
35+
:item="child"
36+
@focusout="
37+
isLastItemOfArray(child, item.children) &&
38+
child.children.length === 0 &&
39+
(open = false)
40+
"
41+
/>
42+
43+
<span v-else>{{ child.text }}</span>
44+
</h4>
45+
46+
<ul class="dropdown-subitem-wrapper">
47+
<li
48+
v-for="grandchild in child.children"
49+
:key="grandchild.link"
50+
class="dropdown-subitem"
51+
>
52+
<NavLink
53+
:item="grandchild"
54+
@focusout="
55+
isLastItemOfArray(grandchild, child.children) &&
56+
isLastItemOfArray(child, item.children) &&
57+
(open = false)
58+
"
59+
/>
60+
</li>
61+
</ul>
62+
</template>
63+
64+
<template v-else>
65+
<NavLink
66+
:item="child"
67+
@focusout="
68+
isLastItemOfArray(child, item.children) && (open = false)
69+
"
70+
/>
71+
</template>
72+
</li>
73+
</ul>
74+
</div>
75+
</template>
76+
77+
<script lang="ts">
78+
import { computed, defineComponent, ref, toRefs, watch } from 'vue'
79+
import type { PropType } from 'vue'
80+
import { useRoute } from 'vue-router'
81+
import type { NavGroup, NavItem } from '../../shared'
82+
import NavLink from './_NavLink.vue'
83+
84+
export default defineComponent({
85+
name: 'DropdownLink',
86+
87+
components: {
88+
NavLink,
89+
},
90+
91+
props: {
92+
item: {
93+
type: Object as PropType<NavGroup<NavItem>>,
94+
required: true,
95+
},
96+
},
97+
98+
setup(props) {
99+
const { item } = toRefs(props)
100+
const dropdownAriaLabel = computed(
101+
() => item.value.ariaLabel || item.value.text
102+
)
103+
104+
const open = ref(false)
105+
const route = useRoute()
106+
watch(
107+
() => route.path,
108+
() => {
109+
open.value = false
110+
}
111+
)
112+
113+
/**
114+
* Open the dropdown when user tab and click from keyboard.
115+
*
116+
* Use event.detail to detect tab and click from keyboard.
117+
* The Tab + Click is UIEvent > KeyboardEvent, so the detail is 0.
118+
*
119+
* @see https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
120+
*/
121+
const handleDropdown = (e): void => {
122+
const isTriggerByTab = e.detail === 0
123+
if (isTriggerByTab) {
124+
open.value = !open.value
125+
} else {
126+
open.value = false
127+
}
128+
}
129+
130+
const isLastItemOfArray = (item: unknown, arr: unknown[]): boolean =>
131+
arr[arr.length - 1] === item
132+
133+
return {
134+
open,
135+
dropdownAriaLabel,
136+
handleDropdown,
137+
isLastItemOfArray,
138+
}
139+
},
140+
})
141+
</script>

Diff for: __old/_DropdownTransition.vue

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<template>
2+
<Transition
3+
name="dropdown"
4+
@enter="setHeight"
5+
@after-enter="unsetHeight"
6+
@before-leave="setHeight"
7+
>
8+
<slot />
9+
</Transition>
10+
</template>
11+
12+
<script lang="ts">
13+
import { defineComponent } from 'vue'
14+
15+
export default defineComponent({
16+
name: 'DropdownTransition',
17+
18+
setup() {
19+
const setHeight = (items): void => {
20+
// explicitly set height so that it can be transitioned
21+
items.style.height = items.scrollHeight + 'px'
22+
}
23+
24+
const unsetHeight = (items): void => {
25+
items.style.height = ''
26+
}
27+
28+
return {
29+
setHeight,
30+
unsetHeight,
31+
}
32+
},
33+
})
34+
</script>

Diff for: __old/_NavLink.vue

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<template>
2+
<RouterLink
3+
custom
4+
:to="item.link"
5+
v-slot="{ href, route, navigate, isActive, isExactActive }"
6+
>
7+
<CNavLink :active="isActive" :href="href" @click="navigate">
8+
{{ item.text }}
9+
</CNavLink>
10+
</RouterLink>
11+
<!-- <a
12+
v-else
13+
class="nav-link external"
14+
:href="item.link"
15+
:rel="linkRel"
16+
:target="linkTarget"
17+
:aria-label="linkAriaLabel"
18+
v-bind="$attrs"
19+
>
20+
<slot name="before" />
21+
{{ item.text }}
22+
<OutboundLink v-if="isBlankTarget" />
23+
<slot name="after" />
24+
</a> -->
25+
</template>
26+
27+
<script lang="ts">
28+
import { computed, defineComponent, toRefs } from 'vue'
29+
import type { PropType } from 'vue'
30+
import { useRoute } from 'vue-router'
31+
import { useSiteData } from '@vuepress/client'
32+
import { isLinkHttp, isLinkMailto, isLinkTel } from '@vuepress/shared'
33+
import type { NavLink } from '../../shared'
34+
import { CNavLink } from './../../../../../../src'
35+
36+
export default defineComponent({
37+
name: 'NavLink',
38+
39+
inheritAttrs: false,
40+
41+
props: {
42+
item: {
43+
type: Object as PropType<NavLink>,
44+
required: true,
45+
},
46+
},
47+
48+
setup(props) {
49+
const route = useRoute()
50+
const site = useSiteData()
51+
const { item } = toRefs(props)
52+
53+
// if the link has http protocol
54+
const hasHttpProtocol = computed(() => isLinkHttp(item.value.link))
55+
// if the link has non-http protocol
56+
const hasNonHttpProtocal = computed(
57+
() => isLinkMailto(item.value.link) || isLinkTel(item.value.link),
58+
)
59+
// resolve the `target` attr
60+
const linkTarget = computed(() => {
61+
if (hasNonHttpProtocal.value) return undefined
62+
if (item.value.target) return item.value.target
63+
if (hasHttpProtocol.value) return '_blank'
64+
return undefined
65+
})
66+
// if the `target` attr is '_blank'
67+
const isBlankTarget = computed(() => linkTarget.value === '_blank')
68+
// is `<RouterLink>` or not
69+
const isRouterLink = computed(
70+
() => !hasHttpProtocol.value && !hasNonHttpProtocal.value && !isBlankTarget.value,
71+
)
72+
// resolve the `rel` attr
73+
const linkRel = computed(() => {
74+
if (hasNonHttpProtocal.value) return undefined
75+
if (item.value.rel) return item.value.rel
76+
if (isBlankTarget.value) return 'noopener noreferrer'
77+
return undefined
78+
})
79+
// resolve the `aria-label` attr
80+
const linkAriaLabel = computed(() => item.value.ariaLabel || item.value.text)
81+
82+
// should be active when current route is a subpath of this link
83+
const shouldBeActiveInSubpath = computed(() => {
84+
const localeKeys = Object.keys(site.value.locales)
85+
if (localeKeys.length) {
86+
return !localeKeys.some((key) => key === item.value.link)
87+
}
88+
return item.value.link !== '/'
89+
})
90+
// if this link is active in subpath
91+
const isActiveInSubpath = computed(() => {
92+
if (!shouldBeActiveInSubpath.value) {
93+
return false
94+
}
95+
return route.path.startsWith(item.value.link)
96+
})
97+
98+
// if this link is active
99+
// const isActive = computed(() => {
100+
// if (!isRouterLink.value) {
101+
// return false
102+
// }
103+
// if (item.value.activeMatch) {
104+
// return new RegExp(item.value.activeMatch).test(route.path)
105+
// }
106+
// return isActiveInSubpath.value
107+
// })
108+
109+
return {
110+
CNavLink,
111+
// isActive,
112+
isBlankTarget,
113+
isRouterLink,
114+
linkRel,
115+
linkTarget,
116+
linkAriaLabel,
117+
}
118+
},
119+
})
120+
</script>

0 commit comments

Comments
 (0)