diff options
| author | 2023-04-19 17:30:39 +0800 | |
|---|---|---|
| committer | 2023-04-19 17:30:39 +0800 | |
| commit | 3adc965dd09490b7efa1cce9f09b0a3b30970277 (patch) | |
| tree | f813abb07d7b003984aa74e3154752b6ffc3ccd5 /docs/app | |
| parent | c7c9ca6f0c8eddf6d34cd40779f3b2d9463f3a46 (diff) | |
| download | HydroRoll-3adc965dd09490b7efa1cce9f09b0a3b30970277.tar.gz HydroRoll-3adc965dd09490b7efa1cce9f09b0a3b30970277.zip | |
✨优化文档
Diffstat (limited to 'docs/app')
| -rw-r--r-- | docs/app/integrations/docsearch.ts | 176 | ||||
| -rw-r--r-- | docs/app/module.ts | 25 | ||||
| -rw-r--r-- | docs/app/router.options.ts | 38 |
3 files changed, 239 insertions, 0 deletions
diff --git a/docs/app/integrations/docsearch.ts b/docs/app/integrations/docsearch.ts new file mode 100644 index 0000000..93ca0e7 --- /dev/null +++ b/docs/app/integrations/docsearch.ts @@ -0,0 +1,176 @@ +import { withoutTrailingSlash } from 'ufo' +import type { DocSearchOptions } from '@nuxtjs/algolia/dist/module.d' + +export default defineNuxtPlugin(() => { + const config = useRuntimeConfig() + + const docSearchElement = ref() + + const hasDocSearch = computed(() => config?.public?.algolia?.docSearch) + + // Setup Algolia DocSearch integration + if (hasDocSearch.value) { + const route = useRoute() + + const router = useRouter() + + /** + * Try to grab options from runtimeConfig. + * + * If not found, fallback on props. + */ + const options = computed<DocSearchOptions>(() => { + const { algolia } = useRuntimeConfig() + + if (algolia && algolia.docSearch) { + return algolia + } + + return {} + }) + + /** + * Check if event is special click to avoid closing the DocSearch too soon. + */ + const isSpecialClick = (event: MouseEvent) => event.button === 1 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey + + /** + * Gets the relative path from an absolute URL provided by the DocSearch instance. + */ + const getRelativePath = (absoluteUrl: string) => { + const { pathname, hash } = new URL(absoluteUrl) + const url = window.location.origin + const relativeUrl = pathname.replace(url, '/') + return withoutTrailingSlash(relativeUrl) + hash + } + + /** + * Initialize the DocSearch instance. + * @param userOptions + */ + const initialize = async (userOptions: any & DocSearchOptions) => { + const el = document.createElement('div') + el.id = '#docsearch-container' + el.style.width = '0' + el.style.height = '0' + el.style.display = 'none' + document.body.appendChild(el) + docSearchElement.value = el + + // @ts-expect-errors - Import @docsearch at runtime + const docsearch = await Promise.all([import(/* webpackChunkName: "docsearch" */ '@docsearch/js'), import(/* webpackChunkName: "docsearch" */ '@docsearch/css')]).then( + ([docsearch]) => docsearch.default + ) + + // TODO: Maybe bind this with @nuxt/i18n ? + // Resolve lang + const lang = userOptions?.lang || 'en' + + // Generate lang prefix + const langPrefix = `${userOptions.langAttribute || 'language'}:${lang}` + + // Get facet filters + const userFacetFilters = userOptions.docSearch.facetFilters || [] + + // Create DocSearch instance + docsearch({ + /** + * Local implementation of this DocSearch box uses a local element with an `docsearch` id. + */ + container: el, + appId: userOptions.applicationId, + apiKey: userOptions.apiKey, + indexName: userOptions.docSearch.indexName, + searchParameters: { + ...(!lang + ? { + facetFilters: userFacetFilters + } + : { + facetFilters: [langPrefix].concat(userFacetFilters) + }), + ...userOptions.searchParameters + }, + /** + * Transform items into relative URL format (compatibility with Vue Router). + */ + transformItems: userOptions.transformItems + ? userOptions.transformItems + : (items) => { + return items.map((item) => { + return { + ...item, + url: getRelativePath(item.url) + } + }) + }, + navigator: userOptions.navigator + ? userOptions.navigator + : { + navigate: ({ itemUrl }) => { + const { pathname: hitPathname } = new URL(window.location.origin + itemUrl) + // Vue Router doesn't handle same-page navigation so we use + // the native browser location API for anchor navigation. + if (route.path === hitPathname) { + window.location.assign(window.location.origin + itemUrl) + } else { + router.push(itemUrl) + } + } + }, + hitComponent: userOptions.hitComponent + ? userOptions.hitComponent + : ({ hit, children }) => { + return { + type: 'a', + constructor: undefined, + __v: 1, + props: { + href: hit.url, + children, + onClick: (event: MouseEvent) => { + if (isSpecialClick(event)) { + return + } + // We rely on the native link scrolling when user is + // already on the right anchor because Vue Router doesn't + // support duplicated history entries. + if (route.fullPath === hit.url) { + return + } + const { pathname: hitPathname } = new URL(window.location.origin + hit.url) + // If the hits goes to another page, we prevent the native link behavior + // to leverage the Vue Router loading feature. + if (route.path !== hitPathname) { + event.preventDefault() + } + router.push(hit.url) + } + } + } + }, + // Spread user options, except the ones that are already used in the instance. + ...Object.entries(userOptions) + // Skip already used keys + .filter(([key]) => !['applicationId', 'apiKey', 'indexName', 'transformItems', 'navigator', 'hitComponent', 'facetFilters', 'langAttribute', 'lang'].includes(key)) + // Recompose options + .reduce((acc: any, [key, value]) => { + acc[key] = value + return acc + }, {}) + }) + } + + // Watch options and restart the instance if needed. + if (process.client) { initialize(options.value) } + } + + return { + provide: { + docSearch: { + element: docSearchElement, + hasDocSearch + } + } + } +}) diff --git a/docs/app/module.ts b/docs/app/module.ts new file mode 100644 index 0000000..4e425a6 --- /dev/null +++ b/docs/app/module.ts @@ -0,0 +1,25 @@ +import { fileURLToPath } from 'url' +import { addPlugin, defineNuxtModule } from '@nuxt/kit' +import { resolve } from 'pathe' + +const themeDir = fileURLToPath(new URL('./', import.meta.url)) +const resolveThemeDir = (path: string) => resolve(themeDir, path) + +export default defineNuxtModule({ + meta: { + name: 'docus', + version: '3.0.0', + compatibility: { + nuxt: '^3.0.0-rc.14', + bridge: false + }, + configKey: 'docus' + }, + setup (_, nuxt) { + if (nuxt.options?.runtimeConfig?.public?.algolia?.docSearch) { + addPlugin({ + src: resolveThemeDir('integrations/docsearch.ts') + }) + } + } +}) diff --git a/docs/app/router.options.ts b/docs/app/router.options.ts new file mode 100644 index 0000000..9380f05 --- /dev/null +++ b/docs/app/router.options.ts @@ -0,0 +1,38 @@ +import type { RouterConfig } from '@nuxt/schema' +// https://router.vuejs.org/api/interfaces/routeroptions.html +export default <RouterConfig> { + scrollBehavior (to, _form, savedPosition) { + if (history.state.stop) { return } + + if (history.state.smooth) { + return { + el: history.state.smooth, + behavior: 'smooth' + } + } + + if (to.hash) { + const el = document.querySelector(to.hash) as any + + if (!el) { return } + + const { marginTop } = getComputedStyle(el) + + const marginTopValue = parseInt(marginTop) + + const offset = (document.querySelector(to.hash) as any).offsetTop - marginTopValue + + return { + top: offset, + behavior: 'smooth' + } + } + + // Scroll to top of window + if (savedPosition) { + return savedPosition + } else { + return { top: 0 } + } + } +} |
