test/node_modules/@vuepress/theme-default/lib/client/composables/useSidebarItems.js
2024-08-13 09:27:52 +08:00

154 lines
5.7 KiB
JavaScript

import { useThemeLocaleData } from '@theme/useThemeData';
import { getHeaders, keys, startsWith } from '@vuepress/helper/client';
import { computed, inject, onMounted, provide, ref, watch } from 'vue';
import { usePageData, usePageFrontmatter, useRoute, useRouteLocale, useRouter, } from 'vuepress/client';
import { isPlainObject, isString } from 'vuepress/shared';
import { getAutoLink, isLinkInternal, resolvePrefix } from '../utils/index.js';
export const headersRef = ref([]);
export const setupHeaders = () => {
const router = useRouter();
const themeLocale = useThemeLocaleData();
const frontmatter = usePageFrontmatter();
const levels = computed(() => frontmatter.value.sidebarDepth ?? themeLocale.value.sidebarDepth ?? 2);
router.beforeEach((to, from) => {
if (to.path !== from.path) {
headersRef.value = [];
}
});
const updateHeaders = () => {
if (levels.value <= 0) {
headersRef.value = [];
return;
}
headersRef.value = getHeaders({
selector: [...new Array(6)]
.map((_, i) => `.theme-default-content h${i + 1}`)
.join(','),
levels: [2, levels.value + 1],
ignore: ['.vp-badge'],
});
};
watch(levels, updateHeaders);
onMounted(updateHeaders);
};
export const useHeaders = () => headersRef;
export const sidebarItemsSymbol = Symbol('sidebarItems');
/**
* Inject sidebar items global computed
*/
export const useSidebarItems = () => {
const sidebarItems = inject(sidebarItemsSymbol);
if (!sidebarItems) {
throw new Error('useSidebarItems() is called without provider.');
}
return sidebarItems;
};
/**
* Create sidebar items ref and provide as global computed in setup
*/
export const setupSidebarItems = () => {
const themeLocale = useThemeLocaleData();
const frontmatter = usePageFrontmatter();
const page = usePageData();
const route = useRoute();
const routeLocale = useRouteLocale();
const headers = useHeaders();
const sidebarConfig = computed(() => frontmatter.value.home
? false
: (frontmatter.value.sidebar ?? themeLocale.value.sidebar ?? 'heading'));
const sidebarItems = computed(() => resolveSidebarItems(sidebarConfig.value, page.value, route.path, routeLocale.value, headers.value));
provide(sidebarItemsSymbol, sidebarItems);
};
/**
* Resolve sidebar items global computed
*
* It should only be resolved and provided once
*/
export const resolveSidebarItems = (sidebarConfig, page, path, routeLocale, headers) => {
// resolve sidebar items according to the config
if (sidebarConfig === false) {
return [];
}
if (sidebarConfig === 'heading') {
return resolveSidebarHeadingItem(page, headers);
}
if (Array.isArray(sidebarConfig)) {
return resolveArraySidebarItems(sidebarConfig, headers, path, routeLocale);
}
if (isPlainObject(sidebarConfig)) {
return resolveMultiSidebarItems(sidebarConfig, page, headers, path);
}
return [];
};
/**
* Util to transform page header to sidebar item
*/
export const resolveSidebarHeaderItem = (header) => ({
text: header.title,
link: header.link,
children: resolveSidebarHeaderItems(header.children),
});
export const resolveSidebarHeaderItems = (headers) => headers ? headers.map((header) => resolveSidebarHeaderItem(header)) : [];
/**
* Resolve current page and its header to sidebar items if the config is `heading`
*/
export const resolveSidebarHeadingItem = (page, headers) => [
{
text: page.title,
children: resolveSidebarHeaderItems(headers),
},
];
/**
* Resolve sidebar items if the config is an array
*/
export const resolveArraySidebarItems = (sidebarConfig, headers, path, prefix = '') => {
const handleChildItem = (item, pathPrefix) => {
const childItem = isString(item)
? getAutoLink(resolvePrefix(pathPrefix, item))
: isString(item.link)
? {
...item,
link: isLinkInternal(item.link)
? getAutoLink(resolvePrefix(pathPrefix, item.link)).link
: item.link,
}
: item;
if ('children' in childItem) {
return {
...childItem,
children: childItem.children.map((item) => handleChildItem(item, resolvePrefix(pathPrefix, childItem.prefix))),
};
}
// if the sidebar item is current page and children is not set
// use headers of current page as children
if (childItem.link === path) {
// skip h1 header
const currentHeaders = headers[0]?.level === 1 ? headers[0].children : headers;
return {
...childItem,
children: resolveSidebarHeaderItems(currentHeaders),
};
}
return childItem;
};
return sidebarConfig.map((item) => handleChildItem(item, prefix));
};
/**
* Resolve sidebar items if the config is a key -> value (path-prefix -> array) object
*/
export const resolveMultiSidebarItems = (sidebarConfig, page, headers, path) => {
const sidebarRoutes = keys(sidebarConfig).sort((x, y) => y.length - x.length);
// Find matching config
for (const base of sidebarRoutes)
if (startsWith(decodeURI(path), base)) {
const matched = sidebarConfig[base];
return matched
? matched === 'heading'
? resolveSidebarHeadingItem(page, headers)
: resolveArraySidebarItems(matched, headers, path, base)
: [];
}
console.warn(`${decodeURI(path)} is missing sidebar config.`);
return [];
};