Skip to content

Commit e974eae

Browse files
authored
Add Tailwind UI results to search (tailwindlabs#1684)
* Add Tailwind UI to search * Replace "Tailwind UI" with "Components" in "recent" list * Update icon * Use original index
1 parent 2105ea1 commit e974eae

File tree

2 files changed

+134
-52
lines changed

2 files changed

+134
-52
lines changed

src/components/Search.js

Lines changed: 116 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ const INDEX_NAME = 'tailwindcss'
1111
const API_KEY = '5fc87cef58bb80203d2207578309fab6'
1212
const APP_ID = 'KNPXZI5B0M'
1313

14+
function isTailwindUIURL(url) {
15+
return url.startsWith('https://tailwindui.com')
16+
}
17+
18+
function isExternalURL(url) {
19+
return url.startsWith('https://')
20+
}
21+
1422
const SearchContext = createContext()
1523

1624
export function SearchProvider({ children }) {
@@ -40,6 +48,22 @@ export function SearchProvider({ children }) {
4048
onClose,
4149
})
4250

51+
useEffect(() => {
52+
// Prepend "Components" to Tailwind UI results that are shown in the "recent" view
53+
if (!isOpen) {
54+
let key = `__DOCSEARCH_RECENT_SEARCHES__${INDEX_NAME}`
55+
try {
56+
let data = JSON.parse(window.localStorage.getItem(key))
57+
for (let item of data) {
58+
if (isTailwindUIURL(item.url) && !item.hierarchy.lvl1.startsWith('Components')) {
59+
item.hierarchy.lvl1 = `Components / ${item.hierarchy.lvl1}`
60+
}
61+
}
62+
window.localStorage.setItem(key, JSON.stringify(data))
63+
} catch {}
64+
}
65+
}, [isOpen])
66+
4367
return (
4468
<>
4569
<Head>
@@ -57,59 +81,97 @@ export function SearchProvider({ children }) {
5781
</SearchContext.Provider>
5882
{isOpen &&
5983
createPortal(
60-
<DocSearchModal
61-
initialQuery={initialQuery}
62-
initialScrollY={window.scrollY}
63-
searchParameters={{
64-
facetFilters: 'version:v3',
65-
distinct: 1,
84+
<div
85+
onClick={(event) => {
86+
let link = event.target.closest('a')
87+
if (!link) return
88+
if (isExternalURL(link.href) && link.target !== '_blank') {
89+
event.preventDefault()
90+
window.open(link.href, '_blank')
91+
}
6692
}}
67-
placeholder="Search documentation"
68-
onClose={onClose}
69-
indexName={INDEX_NAME}
70-
apiKey={API_KEY}
71-
appId={APP_ID}
72-
navigator={{
73-
navigate({ itemUrl }) {
74-
setIsOpen(false)
75-
router.push(itemUrl)
76-
},
77-
}}
78-
hitComponent={Hit}
79-
transformItems={(items) => {
80-
return items.map((item, index) => {
81-
// We transform the absolute URL into a relative URL to
82-
// leverage Next's preloading.
83-
const a = document.createElement('a')
84-
a.href = item.url
85-
86-
const hash = a.hash === '#content-wrapper' || a.hash === '#header' ? '' : a.hash
87-
88-
if (item.hierarchy?.lvl0) {
89-
item.hierarchy.lvl0 = item.hierarchy.lvl0.replace(/&amp;/g, '&')
90-
}
91-
92-
if (item._highlightResult?.hierarchy?.lvl0?.value) {
93-
item._highlightResult.hierarchy.lvl0.value =
94-
item._highlightResult.hierarchy.lvl0.value.replace(/&amp;/g, '&')
95-
}
96-
97-
return {
98-
...item,
99-
url: `${a.pathname}${hash}`,
100-
__is_result: () => true,
101-
__is_parent: () => item.type === 'lvl1' && items.length > 1 && index === 0,
102-
__is_child: () =>
103-
item.type !== 'lvl1' &&
104-
items.length > 1 &&
105-
items[0].type === 'lvl1' &&
106-
index !== 0,
107-
__is_first: () => index === 1,
108-
__is_last: () => index === items.length - 1 && index !== 0,
109-
}
110-
})
111-
}}
112-
/>,
93+
>
94+
<DocSearchModal
95+
initialQuery={initialQuery}
96+
initialScrollY={window.scrollY}
97+
searchParameters={{
98+
facetFilters: 'version:v3',
99+
distinct: 1,
100+
attributesToRetrieve: [
101+
'hierarchy.lvl0',
102+
'hierarchy.lvl1',
103+
'hierarchy.lvl2',
104+
'hierarchy.lvl3',
105+
'hierarchy.lvl4',
106+
'hierarchy.lvl5',
107+
'hierarchy.lvl6',
108+
'content',
109+
'type',
110+
'url',
111+
'product',
112+
'product_category',
113+
],
114+
}}
115+
placeholder="Search documentation"
116+
onClose={onClose}
117+
indexName={INDEX_NAME}
118+
apiKey={API_KEY}
119+
appId={APP_ID}
120+
navigator={{
121+
navigate({ itemUrl }) {
122+
setIsOpen(false)
123+
if (isExternalURL(itemUrl)) {
124+
window.open(itemUrl, '_blank')
125+
} else {
126+
router.push(itemUrl)
127+
}
128+
},
129+
}}
130+
hitComponent={Hit}
131+
transformItems={(items) => {
132+
return items.map((item, index) => {
133+
// We transform the absolute URL into a relative URL to
134+
// leverage Next's preloading.
135+
const a = document.createElement('a')
136+
a.href = item.url
137+
138+
const hash = a.hash === '#content-wrapper' || a.hash === '#header' ? '' : a.hash
139+
140+
if (item.hierarchy?.lvl0) {
141+
item.hierarchy.lvl0 = item.hierarchy.lvl0.replace(/&amp;/g, '&')
142+
}
143+
144+
if (item._highlightResult?.hierarchy?.lvl0?.value) {
145+
item._highlightResult.hierarchy.lvl0.value =
146+
item._highlightResult.hierarchy.lvl0.value.replace(/&amp;/g, '&')
147+
}
148+
149+
let isTailwindUI = isTailwindUIURL(item.url)
150+
151+
return {
152+
...item,
153+
hierarchy: {
154+
...item.hierarchy,
155+
...(isTailwindUI
156+
? { lvl1: `${item.product} / ${item.product_category}` }
157+
: {}),
158+
},
159+
url: isTailwindUI ? item.url.split('#')[0] : `${a.pathname}${hash}`,
160+
__is_result: () => true,
161+
__is_parent: () => item.type === 'lvl1' && items.length > 1 && index === 0,
162+
__is_child: () =>
163+
item.type !== 'lvl1' &&
164+
items.length > 1 &&
165+
items[0].type === 'lvl1' &&
166+
index !== 0,
167+
__is_first: () => index === 1,
168+
__is_last: () => index === items.length - 1 && index !== 0,
169+
__is_tailwindui: () => isTailwindUI,
170+
}
171+
})
172+
}}
173+
/>
174+
</div>,
113175
document.body
114176
)}
115177
</>
@@ -120,12 +182,14 @@ function Hit({ hit, children }) {
120182
return (
121183
<Link
122184
href={hit.url}
185+
target={hit.__is_tailwindui?.() ? '_blank' : undefined}
123186
className={clsx({
124187
'DocSearch-Hit--Result': hit.__is_result?.(),
125188
'DocSearch-Hit--Parent': hit.__is_parent?.(),
126189
'DocSearch-Hit--FirstChild': hit.__is_first?.(),
127190
'DocSearch-Hit--LastChild': hit.__is_last?.(),
128191
'DocSearch-Hit--Child': hit.__is_child?.(),
192+
'DocSearch-Hit--TailwindUI': hit.__is_tailwindui?.(),
129193
})}
130194
>
131195
{children}

src/css/docsearch.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@
267267

268268
.DocSearch-Hit-action {
269269
@apply w-6 h-6;
270+
background-repeat: no-repeat;
271+
background-position: center;
270272
background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m11 9 3 3-3 3' stroke='%23475569' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
271273
}
272274

@@ -523,3 +525,19 @@
523525
.DocSearch-Hit-action + .DocSearch-Hit-action {
524526
@apply ml-3 pl-3 border-l border-slate-200 dark:border-slate-200/5;
525527
}
528+
529+
.DocSearch-Hit--TailwindUI .DocSearch-Hit-icon {
530+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='m6 9 6-3 6 3v6l-6 3-6-3V9Z' fill='%23e0e7ff' /%3E%3Cpath d='m6 9 6 3v7l-6-3V9Z' fill='%23a5b4fc' /%3E%3Cpath d='m18 9-6 3v7l6-3V9Z' fill='%23818cf8' /%3E%3C/svg%3E");
531+
}
532+
533+
.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit--TailwindUI .DocSearch-Hit-icon {
534+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='m6 9 6-3 6 3v6l-6 3-6-3V9Z' fill='%23fff' fill-opacity='.5' /%3E%3Cpath d='m6 9 6 3v7l-6-3V9Z' fill='%23fff' fill-opacity='.6' /%3E%3Cpath d='m18 9-6 3v7l6-3V9Z' fill='%23fff' /%3E%3C/svg%3E");
535+
}
536+
537+
.DocSearch-Hit--TailwindUI .DocSearch-Hit-action {
538+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' fill='none'%3E%3Cpath stroke='%23475569' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 1h3m0 0v3m0-3L5 5M3.5 1H3a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-.5'/%3E%3C/svg%3E");
539+
}
540+
541+
.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit--TailwindUI .DocSearch-Hit-action {
542+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' fill='none'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 1h3m0 0v3m0-3L5 5M3.5 1H3a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-.5'/%3E%3C/svg%3E");
543+
}

0 commit comments

Comments
 (0)