Skip to content

Commit 59b5c3c

Browse files
committed
refactor: extract reusable button component
1 parent 6c271b3 commit 59b5c3c

8 files changed

Lines changed: 85 additions & 18 deletions

File tree

app/components/Button/Base.vue

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script setup lang="ts">
2+
const props = defineProps<{
3+
disabled?: boolean
4+
/**
5+
* type should never be used, because this will always be a button.
6+
*
7+
* If you want a link use `TagLink` instead.
8+
* */
9+
type?: never
10+
}>()
11+
12+
const el = ref<HTMLButtonElement | null>(null)
13+
14+
defineExpose({
15+
focus: () => el.value?.focus(),
16+
})
17+
</script>
18+
19+
<template>
20+
<button
21+
ref="el"
22+
class="btn"
23+
:class="{
24+
'opacity-50 cursor-not-allowed': disabled,
25+
}"
26+
type="button"
27+
:disabled="disabled ? true : undefined"
28+
>
29+
<slot />
30+
</button>
31+
</template>

app/components/Button/Link.vue

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script setup lang="ts">
2+
import type { NuxtLinkProps } from '#app'
3+
4+
const props = defineProps<
5+
{
6+
/** Disabled links will be displayed as plain text */
7+
disabled?: boolean
8+
/**
9+
* `type` should never be used, because this will always be a link.
10+
*
11+
* If you want a button use `TagButton` instead.
12+
* */
13+
type?: never
14+
} &
15+
/** This makes sure the link always has either `to` or `href` */
16+
(Required<Pick<NuxtLinkProps, 'to'>> | Required<Pick<NuxtLinkProps, 'href'>>) &
17+
NuxtLinkProps
18+
>()
19+
</script>
20+
21+
<template>
22+
<!-- This is only a placeholder implementation yet. It will probably need some additional styling, but note: A disabled link is just text. -->
23+
<span v-if="disabled" class="opacity-50"><slot /></span>
24+
<NuxtLink
25+
v-else
26+
class="btn"
27+
:class="
28+
{
29+
'opacity-50 cursor-not-allowed': disabled,
30+
}
31+
"
32+
>
33+
<slot />
34+
</NuxtLink>
35+
</template>

app/components/ColumnPicker.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,17 @@ function handleReset() {
6565

6666
<template>
6767
<div class="relative">
68-
<button
68+
<ButtonBase
6969
ref="buttonRef"
70-
type="button"
71-
class="btn-ghost inline-flex items-center gap-1.5 px-3 py-1.5 border border-border rounded-md hover:border-border-hover focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
70+
class="inline-flex items-center gap-1.5 px-3 py-1.5 border border-border rounded-md hover:border-border-hover focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
7271
:aria-expanded="isOpen"
7372
aria-haspopup="true"
7473
:aria-controls="menuId"
7574
@click.stop="isOpen = !isOpen"
7675
>
7776
<span class="i-carbon-column w-4 h-4" aria-hidden="true" />
7877
<span class="font-mono text-sm">{{ $t('filters.columns.title') }}</span>
79-
</button>
78+
</ButtonBase>
8079

8180
<Transition name="dropdown">
8281
<div

app/pages/@[org].vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ defineOgImageComponent('Default', {
234234
<p class="text-fg-muted mb-4">
235235
{{ error?.message ?? $t('org.page.failed_to_load') }}
236236
</p>
237-
<NuxtLink to="/" class="btn">{{ $t('common.go_back_home') }}</NuxtLink>
237+
<ButtonLink to="/">{{ $t('common.go_back_home') }}</ButtonLink>
238238
</div>
239239

240240
<!-- Empty state -->

app/pages/package-code/[...path].vue

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ defineOgImageComponent('Default', {
359359
<!-- Error: no version -->
360360
<div v-if="!version" class="container py-20 text-center">
361361
<p class="text-fg-muted mb-4">{{ $t('code.version_required') }}</p>
362-
<NuxtLink :to="packageRoute()" class="btn">{{ $t('code.go_to_package') }}</NuxtLink>
362+
<ButtonLink :to="packageRoute()">{{ $t('code.go_to_package') }}</ButtonLink>
363363
</div>
364364

365365
<!-- Loading state -->
@@ -371,7 +371,7 @@ defineOgImageComponent('Default', {
371371
<!-- Error state -->
372372
<div v-else-if="treeStatus === 'error'" class="container py-20 text-center" role="alert">
373373
<p class="text-fg-muted mb-4">{{ $t('code.failed_to_load_tree') }}</p>
374-
<NuxtLink :to="packageRoute(version)" class="btn">{{ $t('code.back_to_package') }}</NuxtLink>
374+
<ButtonLink :to="packageRoute(version)">{{ $t('code.back_to_package') }}</ButtonLink>
375375
</div>
376376

377377
<!-- Main content: file tree + file viewer -->
@@ -472,15 +472,15 @@ defineOgImageComponent('Default', {
472472
<p class="text-fg-subtle text-sm mb-4">
473473
{{ $t('code.file_size_warning', { size: formatBytes(currentNode?.size ?? 0) }) }}
474474
</p>
475-
<a
475+
<ButtonLink
476476
:href="`https://cdn.jsdelivr.net/npm/${packageName}@${version}/${filePath}`"
477477
target="_blank"
478478
rel="noopener noreferrer"
479-
class="btn inline-flex items-center gap-2"
479+
class="inline-flex items-center gap-2"
480480
>
481481
{{ $t('code.view_raw') }}
482482
<span class="i-carbon:launch w-4 h-4" />
483-
</a>
483+
</ButtonLink>
484484
</div>
485485

486486
<!-- Loading file content -->
@@ -526,15 +526,15 @@ defineOgImageComponent('Default', {
526526
<div class="i-carbon:warning-alt w-8 h-8 mx-auto text-fg-subtle mb-4" />
527527
<p class="text-fg-muted mb-2">{{ $t('code.failed_to_load') }}</p>
528528
<p class="text-fg-subtle text-sm mb-4">{{ $t('code.unavailable_hint') }}</p>
529-
<a
529+
<ButtonLink
530530
:href="`https://cdn.jsdelivr.net/npm/${packageName}@${version}/${filePath}`"
531531
target="_blank"
532532
rel="noopener noreferrer"
533-
class="btn inline-flex items-center gap-2"
533+
class="inline-flex items-center gap-2"
534534
>
535535
{{ $t('code.view_raw') }}
536536
<span class="i-carbon:launch w-4 h-4" />
537-
</a>
537+
</ButtonLink>
538538
</div>
539539

540540
<!-- Directory listing (when no file selected or viewing a directory) -->

app/pages/package/[...package].vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,7 @@ defineOgImageComponent('Package', {
11511151
<p class="text-fg-muted mb-8">
11521152
{{ error?.message ?? $t('package.not_found_message') }}
11531153
</p>
1154-
<NuxtLink to="/" class="btn">{{ $t('common.go_back_home') }}</NuxtLink>
1154+
<ButtonLink to="/">{{ $t('common.go_back_home') }}</ButtonLink>
11551155
</div>
11561156
</main>
11571157
</template>

app/pages/~[username]/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ defineOgImageComponent('Default', {
224224
<p class="text-fg-muted mb-4">
225225
{{ error?.message ?? $t('user.page.failed_to_load') }}
226226
</p>
227-
<NuxtLink to="/" class="btn">{{ $t('common.go_back_home') }}</NuxtLink>
227+
<ButtonLink to="/">{{ $t('common.go_back_home') }}</ButtonLink>
228228
</div>
229229

230230
<!-- Package list -->

app/pages/~[username]/orgs.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ async function loadOrgs() {
8181
}
8282
}
8383
84+
error.value = $t('header.orgs_dropdown.error')
85+
8486
// Load on mount and when connection status changes
8587
watch(isOwnProfile, loadOrgs, { immediate: true })
8688
@@ -154,9 +156,9 @@ defineOgImageComponent('Default', {
154156
<!-- Not own profile state -->
155157
<div v-else-if="!isOwnProfile" class="py-12 text-center">
156158
<p class="text-fg-muted">{{ $t('user.orgs_page.own_orgs_only') }}</p>
157-
<NuxtLink :to="`/~${npmUser}/orgs`" class="btn mt-4">{{
159+
<ButtonLink :to="`/~${npmUser}/orgs`" class="mt-4">{{
158160
$t('user.orgs_page.view_your_orgs')
159-
}}</NuxtLink>
161+
}}</ButtonLink>
160162
</div>
161163

162164
<!-- Loading state -->
@@ -165,7 +167,7 @@ defineOgImageComponent('Default', {
165167
<!-- Error state -->
166168
<div v-else-if="error" role="alert" class="py-12 text-center">
167169
<p class="text-fg-muted mb-4">{{ error }}</p>
168-
<button type="button" class="btn" @click="loadOrgs">{{ $t('common.try_again') }}</button>
170+
<ButtonBase @click="loadOrgs">{{ $t('common.try_again') }}</ButtonBase>
169171
</div>
170172

171173
<!-- Empty state -->

0 commit comments

Comments
 (0)