Skip to content

Commit 61d0555

Browse files
committed
fix: improve a11y, ux, consistency, load all versions
1 parent e818f2d commit 61d0555

File tree

20 files changed

+370
-77
lines changed

20 files changed

+370
-77
lines changed

app/components/CommandPalette.client.vue

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const modalRef = useTemplateRef<{
2121
const inputRef = useTemplateRef<{
2222
focus: () => void
2323
}>('inputRef')
24+
const nuxtLinkComponent = resolveComponent('NuxtLink')
2425
2526
const activeIndex = shallowRef(-1)
2627
const previouslyFocused = shallowRef<HTMLElement | null>(null)
@@ -65,9 +66,7 @@ function isInputEventTarget(target: EventTarget | null) {
6566
}
6667
6768
function getCommandElements() {
68-
return Array.from(
69-
getDialog()?.querySelectorAll<HTMLButtonElement>('[data-command-item="true"]') ?? [],
70-
)
69+
return Array.from(getDialog()?.querySelectorAll<HTMLElement>('[data-command-item="true"]') ?? [])
7170
}
7271
7372
function focusInput() {
@@ -87,6 +86,45 @@ async function handleCommandSelect(command: CommandPaletteCommand) {
8786
await command.action()
8887
}
8988
89+
function isLinkCommand(command: CommandPaletteCommand) {
90+
return !!(command.to || command.href)
91+
}
92+
93+
function getCommandComponent(command: CommandPaletteCommand) {
94+
if (command.to) return nuxtLinkComponent
95+
if (command.href) return 'a'
96+
return 'button'
97+
}
98+
99+
function getCommandAttrs(command: CommandPaletteCommand) {
100+
if (command.to) {
101+
return {
102+
to: command.to,
103+
}
104+
}
105+
106+
if (command.href) {
107+
return {
108+
href: command.href,
109+
target: command.external ? '_blank' : undefined,
110+
rel: command.external ? 'noopener noreferrer' : undefined,
111+
}
112+
}
113+
114+
return {
115+
type: 'button' as const,
116+
}
117+
}
118+
119+
function handleCommandClick(command: CommandPaletteCommand) {
120+
if (isLinkCommand(command)) {
121+
close()
122+
return
123+
}
124+
125+
void handleCommandSelect(command)
126+
}
127+
90128
function handleGlobalKeydown(event: KeyboardEvent) {
91129
if (event.isComposing) return
92130
@@ -295,17 +333,18 @@ useEventListener(document, 'keydown', handleGlobalKeydown)
295333

296334
<ul class="m-0 flex list-none flex-col gap-1 p-0">
297335
<li v-for="command in group.items" :key="command.id">
298-
<button
299-
type="button"
300-
class="min-h-12 w-full rounded-lg border border-transparent px-3 py-2.5 text-start transition-colors duration-150 hover:border-border/80 hover:bg-bg focus-visible:outline-accent/70"
336+
<component
337+
:is="getCommandComponent(command)"
338+
v-bind="getCommandAttrs(command)"
339+
class="block min-h-12 w-full rounded-lg border border-transparent px-3 py-2 text-start no-underline text-inherit transition-colors duration-150 hover:border-border/80 hover:bg-bg focus-visible:outline-accent/70"
301340
:class="
302341
activeIndex === (commandIndexMap.get(command.id) ?? -1)
303342
? 'border-border/80 bg-bg'
304343
: ''
305344
"
306345
data-command-item="true"
307346
:aria-current="command.active ? 'true' : undefined"
308-
@click="void handleCommandSelect(command)"
347+
@click="handleCommandClick(command)"
309348
@focus="activeIndex = commandIndexMap.get(command.id) ?? -1"
310349
@mouseenter="activeIndex = commandIndexMap.get(command.id) ?? -1"
311350
>
@@ -347,24 +386,25 @@ useEventListener(document, 'keydown', handleGlobalKeydown)
347386
{{ $t('command_palette.links.external') }}
348387
</span>
349388
</span>
350-
</button>
389+
</component>
351390
</li>
352391
</ul>
353392
</section>
354393

355394
<ul v-if="trailingCommands.length" class="m-0 flex list-none flex-col gap-1 p-0">
356395
<li v-for="command in trailingCommands" :key="command.id">
357-
<button
358-
type="button"
359-
class="min-h-12 w-full rounded-xl border border-border/70 bg-bg-subtle/70 px-3 py-2.5 text-start transition-colors duration-150 hover:border-border/80 hover:bg-bg focus-visible:outline-accent/70"
396+
<component
397+
:is="getCommandComponent(command)"
398+
v-bind="getCommandAttrs(command)"
399+
class="block min-h-12 w-full rounded-xl border border-border/70 bg-bg-subtle/70 px-3 py-2 text-start no-underline text-inherit transition-colors duration-150 hover:border-border/80 hover:bg-bg focus-visible:outline-accent/70"
360400
:class="
361401
activeIndex === (commandIndexMap.get(command.id) ?? -1)
362402
? 'border-border/80 bg-bg'
363403
: ''
364404
"
365405
data-command-item="true"
366406
:aria-current="command.active ? 'true' : undefined"
367-
@click="void handleCommandSelect(command)"
407+
@click="handleCommandClick(command)"
368408
@focus="activeIndex = commandIndexMap.get(command.id) ?? -1"
369409
@mouseenter="activeIndex = commandIndexMap.get(command.id) ?? -1"
370410
>
@@ -380,7 +420,7 @@ useEventListener(document, 'keydown', handleGlobalKeydown)
380420
</span>
381421
</span>
382422
</span>
383-
</button>
423+
</component>
384424
</li>
385425
</ul>
386426
</div>

app/components/Package/ExternalLinks.vue

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ const repoProviderIcon = computed((): IconClass => {
5252
return PROVIDER_ICONS[provider] ?? 'i-lucide:code'
5353
})
5454
55+
const repositoryCommandLabel = computed(() => {
56+
if (!repoRef.value) {
57+
return $t('package.links.repo')
58+
}
59+
60+
const provider = repoRef.value.provider ? ` (${repoRef.value.provider})` : ''
61+
return `${$t('package.links.repo')}${provider}: ${repoRef.value.owner}/${repoRef.value.repo}`
62+
})
63+
5564
useCommandPaletteContextCommands(
5665
computed(() => {
5766
const commands: CommandPaletteContextCommandInput[] = []
@@ -61,11 +70,15 @@ useCommandPaletteContextCommands(
6170
commands.push({
6271
id: 'package-link-repo',
6372
group: 'links',
64-
label: repoRef.value
65-
? `${repoRef.value.owner}/${repoRef.value.repo}`
66-
: $t('package.links.repo'),
67-
keywords: [...packageKeywords, $t('package.links.repo')],
73+
label: repositoryCommandLabel.value,
74+
keywords: [
75+
...packageKeywords,
76+
$t('package.links.repo'),
77+
repoRef.value?.provider ?? '',
78+
repoRef.value ? `${repoRef.value.owner}/${repoRef.value.repo}` : '',
79+
],
6880
iconClass: repoProviderIcon.value,
81+
href: repositoryUrl.value,
6982
external: true,
7083
action: () => {
7184
window.open(repositoryUrl.value!, '_blank', 'noopener,noreferrer')
@@ -80,6 +93,7 @@ useCommandPaletteContextCommands(
8093
label: $t('command_palette.package_links.stars'),
8194
keywords: [...packageKeywords, $t('command_palette.package_links.stars')],
8295
iconClass: 'i-lucide:star',
96+
href: starsLink.value,
8397
external: true,
8498
action: () => {
8599
window.open(starsLink.value!, '_blank', 'noopener,noreferrer')
@@ -94,6 +108,7 @@ useCommandPaletteContextCommands(
94108
label: $t('command_palette.package_links.forks'),
95109
keywords: [...packageKeywords, $t('command_palette.package_links.forks')],
96110
iconClass: 'i-lucide:git-fork',
111+
href: forksLink.value,
97112
external: true,
98113
action: () => {
99114
window.open(forksLink.value!, '_blank', 'noopener,noreferrer')
@@ -108,6 +123,7 @@ useCommandPaletteContextCommands(
108123
label: $t('package.links.homepage'),
109124
keywords: [...packageKeywords, $t('package.links.homepage')],
110125
iconClass: 'i-lucide:link',
126+
href: homepageUrl.value,
111127
external: true,
112128
action: () => {
113129
window.open(homepageUrl.value!, '_blank', 'noopener,noreferrer')
@@ -122,6 +138,7 @@ useCommandPaletteContextCommands(
122138
label: $t('package.links.issues'),
123139
keywords: [...packageKeywords, $t('package.links.issues')],
124140
iconClass: 'i-lucide:circle-alert',
141+
href: displayVersion.value!.bugs!.url!,
125142
external: true,
126143
action: () => {
127144
window.open(displayVersion.value!.bugs!.url!, '_blank', 'noopener,noreferrer')
@@ -135,6 +152,7 @@ useCommandPaletteContextCommands(
135152
label: 'npm',
136153
keywords: [...packageKeywords, $t('common.view_on.npm')],
137154
iconClass: 'i-simple-icons:npm',
155+
href: `https://www.npmjs.com/package/${props.pkg.name}`,
138156
external: true,
139157
action: () => {
140158
window.open(
@@ -152,6 +170,7 @@ useCommandPaletteContextCommands(
152170
label: $t('package.links.jsr'),
153171
keywords: [...packageKeywords, $t('package.links.jsr')],
154172
iconClass: 'i-simple-icons:jsr',
173+
href: props.jsrInfo.url,
155174
external: true,
156175
action: () => {
157176
window.open(props.jsrInfo!.url!, '_blank', 'noopener,noreferrer')
@@ -166,6 +185,7 @@ useCommandPaletteContextCommands(
166185
label: $t('package.links.fund'),
167186
keywords: [...packageKeywords, $t('package.links.fund')],
168187
iconClass: 'i-lucide:heart',
188+
href: fundingUrl.value,
169189
external: true,
170190
action: () => {
171191
window.open(fundingUrl.value!, '_blank', 'noopener,noreferrer')

app/components/Package/Playgrounds.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ useCommandPaletteContextCommands(
126126
label: link.label,
127127
keywords: [link.providerName, $t('package.playgrounds.title')],
128128
iconClass: getIcon(link.provider),
129+
href: link.url,
129130
external: true,
130131
action: () => {
131132
window.open(link.url, '_blank', 'noopener,noreferrer')

app/components/Terminal/Install.vue

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -153,59 +153,60 @@ useCommandPaletteContextCommands(
153153
})
154154
}
155155
156+
if (props.executableInfo?.hasExecutable) {
157+
commands.push({
158+
id: 'package-copy-run',
159+
group: 'package',
160+
label: $t('command_palette.package_actions.copy_run'),
161+
keywords: [props.packageName, $t('package.run.locally')],
162+
iconClass: 'i-lucide:terminal-square',
163+
action: () => {
164+
copyRunCommand(props.executableInfo?.primaryCommand)
165+
},
166+
})
167+
}
168+
169+
if (props.createPackageInfo) {
170+
commands.push({
171+
id: 'package-copy-create',
172+
group: 'package',
173+
label: $t('package.create.copy_command'),
174+
keywords: [props.packageName, props.createPackageInfo.packageName],
175+
iconClass: 'i-lucide:wand-sparkles',
176+
action: () => {
177+
copyCreateCommand()
178+
},
179+
})
180+
}
181+
156182
if (props.typesPackageName && showTypesInInstall.value) {
157183
commands.push({
158184
id: 'package-view-types',
159185
group: 'package',
160186
label: $t('package.get_started.view_types', { package: props.typesPackageName }),
161187
keywords: [props.packageName, props.typesPackageName],
162188
iconClass: 'i-lucide:arrow-right',
189+
to: packageRoute(props.typesPackageName!),
163190
action: async () => {
164191
await navigateTo(packageRoute(props.typesPackageName!))
165192
},
166193
})
167194
}
168195
169-
if (props.executableInfo?.hasExecutable) {
196+
if (props.createPackageInfo) {
170197
commands.push({
171-
id: 'package-copy-run',
198+
id: 'package-open-create-info',
172199
group: 'package',
173-
label: $t('command_palette.package_actions.copy_run'),
174-
keywords: [props.packageName, $t('package.run.locally')],
175-
iconClass: 'i-lucide:terminal-square',
176-
action: () => {
177-
copyRunCommand(props.executableInfo?.primaryCommand)
200+
label: props.createPackageInfo.packageName,
201+
keywords: [props.packageName, props.createPackageInfo.packageName],
202+
iconClass: 'i-lucide:info',
203+
to: packageRoute(props.createPackageInfo.packageName),
204+
action: async () => {
205+
await navigateTo(packageRoute(props.createPackageInfo!.packageName))
178206
},
179207
})
180208
}
181209
182-
if (props.createPackageInfo) {
183-
commands.push(
184-
{
185-
id: 'package-open-create-info',
186-
group: 'package',
187-
label: $t('command_palette.package_actions.open_create', {
188-
packageName: props.createPackageInfo.packageName,
189-
}),
190-
keywords: [props.packageName, props.createPackageInfo.packageName],
191-
iconClass: 'i-lucide:info',
192-
action: async () => {
193-
await navigateTo(packageRoute(props.createPackageInfo!.packageName))
194-
},
195-
},
196-
{
197-
id: 'package-copy-create',
198-
group: 'package',
199-
label: $t('package.create.copy_command'),
200-
keywords: [props.packageName, props.createPackageInfo.packageName],
201-
iconClass: 'i-lucide:wand-sparkles',
202-
action: () => {
203-
copyCreateCommand()
204-
},
205-
},
206-
)
207-
}
208-
209210
return commands
210211
}),
211212
)

0 commit comments

Comments
 (0)