mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 13:17:00 +04:00
98 lines
3.5 KiB
Plaintext
98 lines
3.5 KiB
Plaintext
---
|
|
// Docs search: a native Tabler input that opens the DocSearch modal.
|
|
// Lives in the sidebar, above the menu.
|
|
import Icon from '@ui/Icon.astro'
|
|
---
|
|
|
|
<!-- BEGIN DOCS SEARCH -->
|
|
<div class="input-icon docs-search">
|
|
<span class="input-icon-addon">
|
|
<Icon name="search" />
|
|
</span>
|
|
<input type="search" id="docs-search-input" class="form-control cursor-pointer" placeholder="Search…" aria-label="Search documentation" autocomplete="off" readonly />
|
|
<span class="input-icon-addon">
|
|
<span id="docs-search-kbd" class="fs-4">⌘K</span>
|
|
</span>
|
|
</div>
|
|
<div id="docsearch" class="d-none"></div>
|
|
<!-- END DOCS SEARCH -->
|
|
|
|
<script>
|
|
// Lazy DocSearch: the native input above triggers a dynamic import of
|
|
// @docsearch/js, which Vite splits into its own hashed chunk — nothing is
|
|
// downloaded until the first interaction. DocSearch mounts into the hidden
|
|
// #docsearch container; opening the modal means clicking its hidden button.
|
|
import { DOCSEARCH_APP_ID, DOCSEARCH_INDEX_NAME, DOCSEARCH_API_KEY } from 'astro:env/client'
|
|
|
|
const input = document.getElementById('docs-search-input')
|
|
const mount = document.getElementById('docsearch')
|
|
// Local consts so TypeScript narrows `string | undefined` inside the closures below.
|
|
const appId = DOCSEARCH_APP_ID
|
|
const indexName = DOCSEARCH_INDEX_NAME
|
|
const apiKey = DOCSEARCH_API_KEY
|
|
|
|
if (input && !(appId && indexName && apiKey)) {
|
|
// No Algolia env config (see .env.example) — hide the dead search input.
|
|
input.closest<HTMLElement>('.input-icon')?.classList.add('d-none')
|
|
console.warn('Docs search disabled: DOCSEARCH_* env variables are not set.')
|
|
}
|
|
|
|
if (input && mount && appId && indexName && apiKey) {
|
|
const isMac = /Mac|iPhone|iPad|iPod/.test(navigator.userAgent)
|
|
const kbd = document.getElementById('docs-search-kbd')
|
|
if (kbd && !isMac) kbd.textContent = 'Ctrl K'
|
|
|
|
let state: 'idle' | 'loading' | 'ready' = 'idle'
|
|
let openWhenReady = false
|
|
|
|
const openModal = () => mount.querySelector<HTMLElement>('.DocSearch-Button')?.click()
|
|
|
|
const load = async (open: boolean) => {
|
|
openWhenReady = openWhenReady || open
|
|
if (state === 'ready') {
|
|
if (open) openModal()
|
|
return
|
|
}
|
|
if (state === 'loading') return
|
|
state = 'loading'
|
|
try {
|
|
// The package root is the Ask AI build in v5; /docsearch is plain keyword search.
|
|
const { default: docsearch } = await import('@docsearch/js/docsearch')
|
|
docsearch({
|
|
container: '#docsearch',
|
|
appId,
|
|
// DocSearch 5 takes a list of indices instead of a single indexName.
|
|
indices: [indexName],
|
|
apiKey,
|
|
})
|
|
state = 'ready'
|
|
// The bundle renders its (hidden) button and binds its own shortcuts.
|
|
if (openWhenReady) openModal()
|
|
} catch {
|
|
state = 'idle'
|
|
}
|
|
}
|
|
|
|
input.addEventListener('click', () => {
|
|
load(true)
|
|
input.blur()
|
|
})
|
|
// Keyboard users: prefetch on focus, open on Enter or when they start typing.
|
|
input.addEventListener('focus', () => load(false))
|
|
input.addEventListener('keydown', (event) => {
|
|
if (event.key === 'Enter' || event.key.length === 1) {
|
|
event.preventDefault()
|
|
load(true)
|
|
input.blur()
|
|
}
|
|
})
|
|
input.addEventListener('pointerenter', () => load(false))
|
|
document.addEventListener('keydown', (event) => {
|
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k' && state !== 'ready') {
|
|
event.preventDefault()
|
|
load(true)
|
|
}
|
|
})
|
|
}
|
|
</script>
|