Skip to content

Commit 405364d

Browse files
authored
Merge branch 'main' into jg/test-types-app
2 parents 0beb6f1 + 4d77140 commit 405364d

31 files changed

Lines changed: 912 additions & 525 deletions

app/components/AppHeader.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const showMobileMenu = shallowRef(false)
1919
const route = useRoute()
2020
const isMobile = useIsMobile()
2121
const isSearchExpandedManually = shallowRef(false)
22-
const searchBoxRef = shallowRef<{ focus: () => void } | null>(null)
22+
const searchBoxRef = useTemplateRef('searchBoxRef')
2323
2424
// On search page, always show search expanded on mobile
2525
const isOnHomePage = computed(() => route.name === 'index')

app/components/CallToAction.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ const socialLinks = {
1212
{{ $t('about.get_involved.title') }}
1313
</h2>
1414

15-
<div class="grid gap-4 sm:grid-cols-3">
15+
<div class="grid gap-4 sm:grid-cols-3 sm:items-stretch sm:grid-rows-[auto,1fr,auto]">
1616
<a
1717
:href="socialLinks.github"
1818
target="_blank"
1919
rel="noopener noreferrer"
20-
class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200"
20+
class="group grid gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200 sm:grid-rows-subgrid sm:row-span-3"
2121
>
2222
<div class="flex gap-2">
2323
<span class="i-carbon:logo-github shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" />
@@ -40,7 +40,7 @@ const socialLinks = {
4040
:href="socialLinks.discord"
4141
target="_blank"
4242
rel="noopener noreferrer"
43-
class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200"
43+
class="group grid gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200 sm:grid-rows-subgrid sm:row-span-3"
4444
>
4545
<div class="flex gap-2">
4646
<span class="i-carbon:chat shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" />
@@ -63,7 +63,7 @@ const socialLinks = {
6363
:href="socialLinks.bluesky"
6464
target="_blank"
6565
rel="noopener noreferrer"
66-
class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200"
66+
class="group grid gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200 sm:grid-rows-subgrid sm:row-span-3"
6767
>
6868
<div class="flex gap-2">
6969
<span class="i-simple-icons:bluesky shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" />

app/components/Filter/Panel.vue

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import type {
88
} from '#shared/types/preferences'
99
import {
1010
DOWNLOAD_RANGES,
11-
SEARCH_SCOPE_OPTIONS,
12-
SECURITY_FILTER_OPTIONS,
11+
SEARCH_SCOPE_VALUES,
12+
SECURITY_FILTER_VALUES,
1313
UPDATED_WITHIN_OPTIONS,
1414
} from '#shared/types/preferences'
1515
@@ -205,20 +205,20 @@ const hasActiveFilters = computed(() => !!filterSummary.value)
205205
:aria-label="$t('filters.search_scope')"
206206
>
207207
<button
208-
v-for="option in SEARCH_SCOPE_OPTIONS"
209-
:key="option.value"
208+
v-for="scope in SEARCH_SCOPE_VALUES"
209+
:key="scope"
210210
type="button"
211211
class="px-2 py-0.5 text-xs font-mono rounded-sm transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
212212
:class="
213-
filters.searchScope === option.value
213+
filters.searchScope === scope
214214
? 'bg-bg-muted text-fg'
215215
: 'text-fg-muted hover:text-fg'
216216
"
217-
:aria-pressed="filters.searchScope === option.value"
218-
:title="$t(getScopeDescriptionKey(option.value))"
219-
@click="emit('update:searchScope', option.value)"
217+
:aria-pressed="filters.searchScope === scope"
218+
:title="$t(getScopeDescriptionKey(scope))"
219+
@click="emit('update:searchScope', scope)"
220220
>
221-
{{ $t(getScopeLabelKey(option.value)) }}
221+
{{ $t(getScopeLabelKey(scope)) }}
222222
</button>
223223
</div>
224224
</div>
@@ -301,18 +301,18 @@ const hasActiveFilters = computed(() => !!filterSummary.value)
301301
</legend>
302302
<div class="flex flex-wrap gap-2" role="radiogroup" :aria-label="$t('filters.security')">
303303
<button
304-
v-for="option in SECURITY_FILTER_OPTIONS"
305-
:key="option.value"
304+
v-for="security in SECURITY_FILTER_VALUES"
305+
:key="security"
306306
type="button"
307307
role="radio"
308308
disabled
309-
:aria-checked="filters.security === option.value"
309+
:aria-checked="filters.security === security"
310310
class="tag transition-colors duration-200 opacity-50 cursor-not-allowed focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
311311
:class="
312-
filters.security === option.value ? 'bg-fg text-bg border-fg hover:text-bg/70' : ''
312+
filters.security === security ? 'bg-fg text-bg border-fg hover:text-bg/70' : ''
313313
"
314314
>
315-
{{ $t(getSecurityLabelKey(option.value)) }}
315+
{{ $t(getSecurityLabelKey(security)) }}
316316
</button>
317317
</div>
318318
</fieldset>

app/components/Header/SearchBox.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function handleSearchFocus() {
8080
}
8181
8282
// Expose focus method for parent components
83-
const inputRef = shallowRef<HTMLInputElement | null>(null)
83+
const inputRef = useTemplateRef('inputRef')
8484
function focus() {
8585
inputRef.value?.focus()
8686
}

app/components/Modal.client.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const props = defineProps<{
33
modalTitle: string
44
}>()
55
6-
const dialogRef = ref<HTMLDialogElement>()
6+
const dialogRef = useTemplateRef('dialogRef')
77
88
const modalTitleId = computed(() => {
99
const id = getCurrentInstance()?.attrs.id

app/components/Package/ClaimPackageModal.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ async function handleClaim() {
9696
}
9797
}
9898
99-
const dialogRef = ref<HTMLDialogElement>()
99+
const dialogRef = useTemplateRef('dialogRef')
100100
101101
function open() {
102102
// Reset state and check availability each time modal is opened

app/components/compare/FacetSelector.vue

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,36 @@
11
<script setup lang="ts">
2-
import type { ComparisonFacet } from '#shared/types'
3-
import { FACET_INFO, FACETS_BY_CATEGORY, CATEGORY_ORDER } from '#shared/types/comparison'
4-
5-
const { isFacetSelected, toggleFacet, selectCategory, deselectCategory } = useFacetSelection()
6-
7-
// Enrich facets with their info for rendering
8-
const facetsByCategory = computed(() => {
9-
const result: Record<
10-
string,
11-
{ facet: ComparisonFacet; info: (typeof FACET_INFO)[ComparisonFacet] }[]
12-
> = {}
13-
for (const category of CATEGORY_ORDER) {
14-
result[category] = FACETS_BY_CATEGORY[category].map(facet => ({
15-
facet,
16-
info: FACET_INFO[facet],
17-
}))
18-
}
19-
return result
20-
})
2+
const {
3+
isFacetSelected,
4+
toggleFacet,
5+
selectCategory,
6+
deselectCategory,
7+
facetsByCategory,
8+
categoryOrder,
9+
getCategoryLabel,
10+
} = useFacetSelection()
2111
2212
// Check if all non-comingSoon facets in a category are selected
2313
function isCategoryAllSelected(category: string): boolean {
2414
const facets = facetsByCategory.value[category] ?? []
25-
const selectableFacets = facets.filter(f => !f.info.comingSoon)
26-
return selectableFacets.length > 0 && selectableFacets.every(f => isFacetSelected(f.facet))
15+
const selectableFacets = facets.filter(f => !f.comingSoon)
16+
return selectableFacets.length > 0 && selectableFacets.every(f => isFacetSelected(f.id))
2717
}
2818
2919
// Check if no facets in a category are selected
3020
function isCategoryNoneSelected(category: string): boolean {
3121
const facets = facetsByCategory.value[category] ?? []
32-
const selectableFacets = facets.filter(f => !f.info.comingSoon)
33-
return selectableFacets.length > 0 && selectableFacets.every(f => !isFacetSelected(f.facet))
22+
const selectableFacets = facets.filter(f => !f.comingSoon)
23+
return selectableFacets.length > 0 && selectableFacets.every(f => !isFacetSelected(f.id))
3424
}
3525
</script>
3626

3727
<template>
3828
<div class="space-y-3" role="group" :aria-label="$t('compare.facets.group_label')">
39-
<div v-for="category in CATEGORY_ORDER" :key="category">
29+
<div v-for="category in categoryOrder" :key="category">
4030
<!-- Category header with all/none buttons -->
4131
<div class="flex items-center gap-2 mb-2">
4232
<span class="text-[10px] text-fg-subtle uppercase tracking-wider">
43-
{{ $t(`compare.facets.categories.${category}`) }}
33+
{{ getCategoryLabel(category) }}
4434
</span>
4535
<button
4636
type="button"
@@ -51,9 +41,7 @@ function isCategoryNoneSelected(category: string): boolean {
5141
: 'text-fg-muted/60 hover:text-fg-muted'
5242
"
5343
:aria-label="
54-
$t('compare.facets.select_category', {
55-
category: $t(`compare.facets.categories.${category}`),
56-
})
44+
$t('compare.facets.select_category', { category: getCategoryLabel(category) })
5745
"
5846
:disabled="isCategoryAllSelected(category)"
5947
@click="selectCategory(category)"
@@ -70,9 +58,7 @@ function isCategoryNoneSelected(category: string): boolean {
7058
: 'text-fg-muted/60 hover:text-fg-muted'
7159
"
7260
:aria-label="
73-
$t('compare.facets.deselect_category', {
74-
category: $t(`compare.facets.categories.${category}`),
75-
})
61+
$t('compare.facets.deselect_category', { category: getCategoryLabel(category) })
7662
"
7763
:disabled="isCategoryNoneSelected(category)"
7864
@click="deselectCategory(category)"
@@ -84,31 +70,31 @@ function isCategoryNoneSelected(category: string): boolean {
8470
<!-- Facet buttons -->
8571
<div class="flex items-center gap-1.5 flex-wrap" role="group">
8672
<button
87-
v-for="{ facet, info } in facetsByCategory[category]"
88-
:key="facet"
73+
v-for="facet in facetsByCategory[category]"
74+
:key="facet.id"
8975
type="button"
90-
:title="info.comingSoon ? $t('compare.facets.coming_soon') : info.description"
91-
:disabled="info.comingSoon"
92-
:aria-pressed="isFacetSelected(facet)"
93-
:aria-label="info.label"
76+
:title="facet.comingSoon ? $t('compare.facets.coming_soon') : facet.description"
77+
:disabled="facet.comingSoon"
78+
:aria-pressed="isFacetSelected(facet.id)"
79+
:aria-label="facet.label"
9480
class="inline-flex items-center gap-1 px-1.5 py-0.5 font-mono text-xs rounded border transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
9581
:class="
96-
info.comingSoon
82+
facet.comingSoon
9783
? 'text-fg-subtle/50 bg-bg-subtle border-border-subtle cursor-not-allowed'
98-
: isFacetSelected(facet)
84+
: isFacetSelected(facet.id)
9985
? 'text-fg-muted bg-bg-muted border-border'
10086
: 'text-fg-subtle bg-bg-subtle border-border-subtle hover:text-fg-muted hover:border-border'
10187
"
102-
@click="!info.comingSoon && toggleFacet(facet)"
88+
@click="!facet.comingSoon && toggleFacet(facet.id)"
10389
>
10490
<span
105-
v-if="!info.comingSoon"
91+
v-if="!facet.comingSoon"
10692
class="w-3 h-3"
107-
:class="isFacetSelected(facet) ? 'i-carbon:checkmark' : 'i-carbon:add'"
93+
:class="isFacetSelected(facet.id) ? 'i-carbon:checkmark' : 'i-carbon:add'"
10894
aria-hidden="true"
10995
/>
110-
{{ info.label }}
111-
<span v-if="info.comingSoon" class="text-[9px]"
96+
{{ facet.label }}
97+
<span v-if="facet.comingSoon" class="text-[9px]"
11298
>({{ $t('compare.facets.coming_soon') }})</span
11399
>
114100
</button>

0 commit comments

Comments
 (0)