Skip to content

Commit

Permalink
Add Tailwind UI results to search (tailwindlabs#1684)
Browse files Browse the repository at this point in the history
* Add Tailwind UI to search

* Replace "Tailwind UI" with "Components" in "recent" list

* Update icon

* Use original index
  • Loading branch information
bradlc authored Sep 25, 2023
1 parent 2105ea1 commit e974eae
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 52 deletions.
168 changes: 116 additions & 52 deletions src/components/Search.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ const INDEX_NAME = 'tailwindcss'
const API_KEY = '5fc87cef58bb80203d2207578309fab6'
const APP_ID = 'KNPXZI5B0M'

function isTailwindUIURL(url) {
return url.startsWith('https://tailwindui.com')
}

function isExternalURL(url) {
return url.startsWith('https://')
}

const SearchContext = createContext()

export function SearchProvider({ children }) {
Expand Down Expand Up @@ -40,6 +48,22 @@ export function SearchProvider({ children }) {
onClose,
})

useEffect(() => {
// Prepend "Components" to Tailwind UI results that are shown in the "recent" view
if (!isOpen) {
let key = `__DOCSEARCH_RECENT_SEARCHES__${INDEX_NAME}`
try {
let data = JSON.parse(window.localStorage.getItem(key))
for (let item of data) {
if (isTailwindUIURL(item.url) && !item.hierarchy.lvl1.startsWith('Components')) {
item.hierarchy.lvl1 = `Components / ${item.hierarchy.lvl1}`
}
}
window.localStorage.setItem(key, JSON.stringify(data))
} catch {}
}
}, [isOpen])

return (
<>
<Head>
Expand All @@ -57,59 +81,97 @@ export function SearchProvider({ children }) {
</SearchContext.Provider>
{isOpen &&
createPortal(
<DocSearchModal
initialQuery={initialQuery}
initialScrollY={window.scrollY}
searchParameters={{
facetFilters: 'version:v3',
distinct: 1,
<div
onClick={(event) => {
let link = event.target.closest('a')
if (!link) return
if (isExternalURL(link.href) && link.target !== '_blank') {
event.preventDefault()
window.open(link.href, '_blank')
}
}}
placeholder="Search documentation"
onClose={onClose}
indexName={INDEX_NAME}
apiKey={API_KEY}
appId={APP_ID}
navigator={{
navigate({ itemUrl }) {
setIsOpen(false)
router.push(itemUrl)
},
}}
hitComponent={Hit}
transformItems={(items) => {
return items.map((item, index) => {
// We transform the absolute URL into a relative URL to
// leverage Next's preloading.
const a = document.createElement('a')
a.href = item.url

const hash = a.hash === '#content-wrapper' || a.hash === '#header' ? '' : a.hash

if (item.hierarchy?.lvl0) {
item.hierarchy.lvl0 = item.hierarchy.lvl0.replace(/&amp;/g, '&')
}

if (item._highlightResult?.hierarchy?.lvl0?.value) {
item._highlightResult.hierarchy.lvl0.value =
item._highlightResult.hierarchy.lvl0.value.replace(/&amp;/g, '&')
}

return {
...item,
url: `${a.pathname}${hash}`,
__is_result: () => true,
__is_parent: () => item.type === 'lvl1' && items.length > 1 && index === 0,
__is_child: () =>
item.type !== 'lvl1' &&
items.length > 1 &&
items[0].type === 'lvl1' &&
index !== 0,
__is_first: () => index === 1,
__is_last: () => index === items.length - 1 && index !== 0,
}
})
}}
/>,
>
<DocSearchModal
initialQuery={initialQuery}
initialScrollY={window.scrollY}
searchParameters={{
facetFilters: 'version:v3',
distinct: 1,
attributesToRetrieve: [
'hierarchy.lvl0',
'hierarchy.lvl1',
'hierarchy.lvl2',
'hierarchy.lvl3',
'hierarchy.lvl4',
'hierarchy.lvl5',
'hierarchy.lvl6',
'content',
'type',
'url',
'product',
'product_category',
],
}}
placeholder="Search documentation"
onClose={onClose}
indexName={INDEX_NAME}
apiKey={API_KEY}
appId={APP_ID}
navigator={{
navigate({ itemUrl }) {
setIsOpen(false)
if (isExternalURL(itemUrl)) {
window.open(itemUrl, '_blank')
} else {
router.push(itemUrl)
}
},
}}
hitComponent={Hit}
transformItems={(items) => {
return items.map((item, index) => {
// We transform the absolute URL into a relative URL to
// leverage Next's preloading.
const a = document.createElement('a')
a.href = item.url

const hash = a.hash === '#content-wrapper' || a.hash === '#header' ? '' : a.hash

if (item.hierarchy?.lvl0) {
item.hierarchy.lvl0 = item.hierarchy.lvl0.replace(/&amp;/g, '&')
}

if (item._highlightResult?.hierarchy?.lvl0?.value) {
item._highlightResult.hierarchy.lvl0.value =
item._highlightResult.hierarchy.lvl0.value.replace(/&amp;/g, '&')
}

let isTailwindUI = isTailwindUIURL(item.url)

return {
...item,
hierarchy: {
...item.hierarchy,
...(isTailwindUI
? { lvl1: `${item.product} / ${item.product_category}` }
: {}),
},
url: isTailwindUI ? item.url.split('#')[0] : `${a.pathname}${hash}`,
__is_result: () => true,
__is_parent: () => item.type === 'lvl1' && items.length > 1 && index === 0,
__is_child: () =>
item.type !== 'lvl1' &&
items.length > 1 &&
items[0].type === 'lvl1' &&
index !== 0,
__is_first: () => index === 1,
__is_last: () => index === items.length - 1 && index !== 0,
__is_tailwindui: () => isTailwindUI,
}
})
}}
/>
</div>,
document.body
)}
</>
Expand All @@ -120,12 +182,14 @@ function Hit({ hit, children }) {
return (
<Link
href={hit.url}
target={hit.__is_tailwindui?.() ? '_blank' : undefined}
className={clsx({
'DocSearch-Hit--Result': hit.__is_result?.(),
'DocSearch-Hit--Parent': hit.__is_parent?.(),
'DocSearch-Hit--FirstChild': hit.__is_first?.(),
'DocSearch-Hit--LastChild': hit.__is_last?.(),
'DocSearch-Hit--Child': hit.__is_child?.(),
'DocSearch-Hit--TailwindUI': hit.__is_tailwindui?.(),
})}
>
{children}
Expand Down
18 changes: 18 additions & 0 deletions src/css/docsearch.css
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@

.DocSearch-Hit-action {
@apply w-6 h-6;
background-repeat: no-repeat;
background-position: center;
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");
}

Expand Down Expand Up @@ -523,3 +525,19 @@
.DocSearch-Hit-action + .DocSearch-Hit-action {
@apply ml-3 pl-3 border-l border-slate-200 dark:border-slate-200/5;
}

.DocSearch-Hit--TailwindUI .DocSearch-Hit-icon {
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");
}

.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit--TailwindUI .DocSearch-Hit-icon {
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");
}

.DocSearch-Hit--TailwindUI .DocSearch-Hit-action {
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");
}

.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit--TailwindUI .DocSearch-Hit-action {
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");
}

0 comments on commit e974eae

Please sign in to comment.