Skip to content

Commit 6ab6258

Browse files
rijkvanzantenhola-soy-milkhanneskuettner
authored
Switch to algolia search w/ the new index (#126)
* Switch to algolia search w/ the new index * Keep off-site links intact * Match Directus TV * Match directus TV * Trigger Build * Tweak order * Improve logging * Further logging * Use item url * Do some logging * Use item url * More logging * Try without regex * More logging * move logging * Add more logs * Try string replacing * Nasty hack * More logging still * Even more logging * Remove some logging * Remove extra slash * Remove more logging * Return item as-is * Use relative URL * Try and work around nuxt algolia bug * Second time's the charm? * Third time surely is! * four. I meant four * fix: conditionally include UContentSearch only for nuxt search backend --------- Co-authored-by: Carmen Huidobro <[email protected]> Co-authored-by: Hannes Küttner <[email protected]>
1 parent 9c97d4f commit 6ab6258

File tree

5 files changed

+114
-4
lines changed

5 files changed

+114
-4
lines changed

app/app.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export default defineAppConfig({
22
search: {
3-
backend: 'nuxt', // 'nuxt' | 'algolia',
3+
backend: 'algolia', // 'nuxt' | 'algolia',
44
},
55

66
ui: {

app/app.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ defineOgImage({
1010
url: '/img/og-image.png',
1111
});
1212
13+
const { search } = useAppConfig();
14+
1315
const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', { default: () => [], server: false });
1416
</script>
1517

@@ -25,7 +27,7 @@ const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', { defa
2527

2628
<DocsFooter />
2729

28-
<ClientOnly>
30+
<ClientOnly v-if="search.backend === 'nuxt'">
2931
<LazyUContentSearch
3032
ref="searchRef"
3133
:files="files"

app/components/DocsHeader.vue

+80-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<script setup lang="ts">
2+
import type { DocSearchProps } from '@docsearch/react';
23
import type { NavItem } from '@nuxt/content';
34
import type { HeaderLink } from '@nuxt/ui-pro/types';
45
import type { OpenAPIObject } from 'openapi3-ts/oas30';
6+
import { withoutTrailingSlash } from 'ufo';
57
68
const navigation = inject<NavItem[]>('navigation', []);
79
const oasSpec = inject<OpenAPIObject>('openapi', { openapi: '3.0', info: { title: 'OAS Spec', version: '0' }, paths: {} });
@@ -10,6 +12,7 @@ const { metaSymbol } = useShortcuts();
1012
1113
const { header, search } = useAppConfig();
1214
const route = useRoute();
15+
const router = useRouter();
1316
1417
const links = computed(() =>
1518
header.nav.map((link: HeaderLink) => {
@@ -33,6 +36,78 @@ const navigationTree = computed(() => {
3336
return item._path.startsWith(routePrefix);
3437
})?.children ?? []);
3538
});
39+
40+
const isSpecialClick = (event: MouseEvent) =>
41+
event.button === 1 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
42+
43+
const withoutBaseUrl = (url: string) => {
44+
const { app } = useRuntimeConfig();
45+
const routerBase = withoutTrailingSlash(app.baseURL);
46+
const hasBaseURL = routerBase !== '/';
47+
48+
if (hasBaseURL && url.startsWith(routerBase)) {
49+
return url.substring(routerBase.length) || '/';
50+
}
51+
return url;
52+
};
53+
54+
// This is needed until https://github.com/nuxt-modules/algolia/issues/208 is resolved
55+
const algoliaHitComponent: DocSearchProps['hitComponent'] = ({ hit, children }) => {
56+
return {
57+
type: 'a',
58+
constructor: undefined,
59+
__v: 1,
60+
props: {
61+
href: hit.url,
62+
children,
63+
onClick: (event: MouseEvent) => {
64+
if (isSpecialClick(event)) {
65+
return;
66+
}
67+
68+
// We rely on the native link scrolling when user is
69+
// already on the right anchor because Vue Router doesn't
70+
// support duplicated history entries.
71+
if (route.fullPath === hit.url) {
72+
return;
73+
}
74+
75+
if (hit.url.startsWith('https://')) {
76+
// don't prevent native navigation
77+
}
78+
else {
79+
const { pathname: hitPathname } = new URL(window.location.origin + hit.url);
80+
81+
// If the hits goes to another page, we prevent the native link behavior
82+
// to leverage the Vue Router loading feature.
83+
if (route.path !== hitPathname) {
84+
event.preventDefault();
85+
}
86+
87+
router.push(withoutBaseUrl(hit.url));
88+
}
89+
},
90+
},
91+
} as any;
92+
};
93+
94+
const algoliaNavigator = {
95+
navigate: ({ itemUrl }) => {
96+
const isAbsoluteUrl = itemUrl.startsWith('https://');
97+
const { pathname: hitPathname } = new URL(isAbsoluteUrl ? itemUrl : window.location.origin + itemUrl);
98+
// Vue Router doesn't handle same-page navigation so we use
99+
// the native browser location API for anchor navigation.
100+
if (route.path === hitPathname) {
101+
window.location.assign(window.location.origin + itemUrl);
102+
}
103+
else if (isAbsoluteUrl) {
104+
navigateTo(itemUrl, { external: true });
105+
}
106+
else {
107+
router.push(withoutBaseUrl(itemUrl));
108+
}
109+
},
110+
};
36111
</script>
37112

38113
<template>
@@ -59,7 +134,11 @@ const navigationTree = computed(() => {
59134
color="gray"
60135
square
61136
>
62-
<AlgoliaDocSearch />
137+
<AlgoliaDocSearch
138+
:transform-items="transformAlgoliaSearchItems"
139+
:hit-component="algoliaHitComponent"
140+
:navigator="algoliaNavigator"
141+
/>
63142
</UButton>
64143
</UTooltip>
65144
</ClientOnly>
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { DocSearchHit } from "@docsearch/react";
2+
import { withoutTrailingSlash } from "ufo";
3+
4+
function getRelativePath(absoluteUrl: string) {
5+
const { pathname, hash } = new URL(absoluteUrl);
6+
const url = window.location.origin;
7+
const relativeUrl = pathname.replace(url, '/') + hash;
8+
return withoutTrailingSlash(relativeUrl);
9+
}
10+
11+
/**
12+
* Transform Algolia search items to ensure off-site links aren't rendered as relative links
13+
*/
14+
export default function (items: DocSearchHit[]): DocSearchHit[] {
15+
return items.map((item) => {
16+
const relativePath = getRelativePath(item.url);
17+
18+
let url = relativePath;
19+
20+
if (!relativePath.startsWith('/docs')) {
21+
url = item.url;
22+
}
23+
24+
return {
25+
...item,
26+
url,
27+
};
28+
});
29+
}

nuxt.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export default defineNuxtConfig({
130130

131131
algolia: {
132132
docSearch: {
133-
indexName: 'directus',
133+
indexName: 'directus_unified',
134134
},
135135
},
136136

0 commit comments

Comments
 (0)