Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,17 @@ if (import.meta.server) {
}

const keyboardShortcuts = useKeyboardShortcuts()
const { settings } = useSettings()

onKeyDown(
'/',
e => {
if (e.ctrlKey) {
e.preventDefault()
settings.value.instantSearch = !settings.value.instantSearch
return
}

if (!keyboardShortcuts.value || isEditableElement(e.target)) return
Comment thread
knowler marked this conversation as resolved.
e.preventDefault()

Expand Down
54 changes: 54 additions & 0 deletions app/components/InstantSearch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts">
import { useSettings } from '~/composables/useSettings'

const { settings } = useSettings()

onPrehydrate(el => {
const settingsSaved = JSON.parse(localStorage.getItem('npmx-settings') || '{}')
const enabled = settingsSaved.instantSearch
if (enabled === false) {
el.querySelector('[data-instant-search-on]')!.className = 'hidden'
el.querySelector('[data-instant-search-off]')!.className = ''
}
Comment thread
alexdln marked this conversation as resolved.
})
</script>

<template>
<p id="instant-search-advisory" class="text-fg-muted text-sm text-pretty">
<span
class="i-lucide:zap align-middle text-fg relative top-[-0.1em] me-1"
style="font-size: 0.8em"
aria-hidden="true"
/>
<span data-instant-search-on :class="settings.instantSearch ? '' : 'hidden'">
<i18n-t keypath="search.instant_search_advisory">
<template #label>
{{ $t('search.instant_search') }}
</template>
<template #state>
<strong>{{ $t('search.instant_search_on') }}</strong>
</template>
<template #action>
<button type="button" class="underline" @click="settings.instantSearch = false">
{{ $t('search.instant_search_turn_off') }}
</button>
</template>
</i18n-t>
</span>
<span data-instant-search-off :class="settings.instantSearch ? 'hidden' : ''">
<i18n-t keypath="search.instant_search_advisory">
<template #label>
{{ $t('search.instant_search') }}
</template>
<template #state>
<strong>{{ $t('search.instant_search_off') }}</strong>
</template>
<template #action>
<button type="button" class="underline" @click="settings.instantSearch = true">
{{ $t('search.instant_search_turn_on') }}
</button>
</template>
</i18n-t>
</span>
</p>
</template>
16 changes: 13 additions & 3 deletions app/pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<script setup lang="ts">
import { SHOWCASED_FRAMEWORKS } from '~/utils/frameworks'

const { settings } = useSettings()

Check failure on line 4 in app/pages/index.vue

View workflow job for this annotation

GitHub Actions / 💪 Type check

'settings' is declared but its value is never read.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
const { model: searchQuery, startSearch } = useGlobalSearch()
const isSearchFocused = shallowRef(false)

Expand Down Expand Up @@ -52,15 +54,20 @@
{{ $t('tagline') }}
</p>
<search
class="w-full max-w-xl motion-safe:animate-slide-up motion-safe:animate-fill-both"
class="w-full max-w-2xl motion-safe:animate-slide-up motion-safe:animate-fill-both"
style="animation-delay: 0.2s"
>
<form method="GET" action="/search" class="relative" @submit.prevent.trim="search">
<form
method="GET"
action="/search"
class="relative grid justify-items-center gap-4"
@submit.prevent.trim="search"
>
Comment thread
knowler marked this conversation as resolved.
<label for="home-search" class="sr-only">
{{ $t('search.label') }}
</label>

<div class="relative group" :class="{ 'is-focused': isSearchFocused }">
<div class="relative group w-full max-w-xl" :class="{ 'is-focused': isSearchFocused }">
<div
class="absolute z-1 -inset-px pointer-events-none rounded-lg bg-gradient-to-r from-fg/0 to-accent/5 opacity-0 transition-opacity duration-500 blur-sm group-[.is-focused]:opacity-100"
/>
Expand All @@ -82,6 +89,7 @@
no-correct
size="large"
class="w-full ps-8 pe-24"
aria-describedby="instant-search-advisory"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
@focus="isSearchFocused = true"
@blur="isSearchFocused = false"
/>
Expand All @@ -98,6 +106,8 @@
</ButtonBase>
</div>
</div>

<InstantSearch />
</form>
</search>

Expand Down
8 changes: 7 additions & 1 deletion i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,13 @@
"org": "org",
"view_user_packages": "View packages by this user",
"view_org_packages": "View packages by this organization"
}
},
"instant_search": "Instant search",
"instant_search_on": "on",
"instant_search_off": "off",
"instant_search_turn_on": "turn on",
"instant_search_turn_off": "turn off",
"instant_search_advisory": "{label} {state} — {action}"
},
"nav": {
"main_navigation": "Main",
Expand Down
18 changes: 18 additions & 0 deletions i18n/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,24 @@
}
},
"additionalProperties": false
},
"instant_search": {
"type": "string"
},
"instant_search_on": {
"type": "string"
},
"instant_search_off": {
"type": "string"
},
"instant_search_turn_on": {
"type": "string"
},
"instant_search_turn_off": {
"type": "string"
},
"instant_search_advisory": {
"type": "string"
}
},
"additionalProperties": false
Expand Down
9 changes: 9 additions & 0 deletions test/nuxt/a11y.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ import {
HeaderAccountMenu,
HeaderConnectorModal,
HeaderSearchBox,
InstantSearch,
InputBase,
LicenseDisplay,
LoadingSpinner,
Expand Down Expand Up @@ -2657,6 +2658,14 @@ describe('component accessibility audits', () => {
})
})

describe('InstantSearch', () => {
it('should have no accessibility violations', async () => {
const component = await mountSuspended(InstantSearch)
const results = await runAxe(component)
expect(results.violations).toEqual([])
})
})

describe('SearchProviderToggle', () => {
it('should have no accessibility violations', async () => {
const component = await mountSuspended(SearchProviderToggle)
Expand Down
Loading