-
-
Notifications
You must be signed in to change notification settings - Fork 424
Expand file tree
/
Copy pathPackageManagerTabs.vue
More file actions
97 lines (84 loc) · 3.03 KB
/
PackageManagerTabs.vue
File metadata and controls
97 lines (84 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<script setup lang="ts">
const selectedPM = useSelectedPackageManager()
const tablistNavigationKeys = new Set(['ArrowRight', 'ArrowLeft', 'Home', 'End'])
function onTabListKeydown(event: KeyboardEvent) {
if (!tablistNavigationKeys.has(event.key)) return
const tablist = event.currentTarget as HTMLElement | null
if (!tablist) return
const tabs = Array.from(tablist.querySelectorAll<HTMLElement>('[role="tab"]'))
const count = Math.min(tabs.length, packageManagers.length)
if (!count) return
event.preventDefault()
let activeIndex = packageManagers.findIndex(pm => pm.id === selectedPM.value)
if (activeIndex < 0) activeIndex = 0
let nextIndex = activeIndex
if (event.key === 'ArrowRight') nextIndex = (activeIndex + 1) % count
if (event.key === 'ArrowLeft') nextIndex = (activeIndex - 1 + count) % count
if (event.key === 'Home') nextIndex = 0
if (event.key === 'End') nextIndex = count - 1
const nextTab = tabs[nextIndex]
const nextId = packageManagers[nextIndex]?.id
if (nextId && nextId !== selectedPM.value) {
selectedPM.value = nextId
}
nextTick(() => nextTab?.focus())
}
</script>
<template>
<div
class="flex items-center gap-1 p-0.5 bg-bg-subtle border border-border-subtle rounded-md overflow-x-auto"
role="tablist"
:aria-label="$t('package.get_started.pm_label')"
@keydown="onTabListKeydown"
>
<button
v-for="pm in packageManagers"
:key="pm.id"
:id="`pm-tab-${pm.id}`"
role="tab"
:data-pm-tab="pm.id"
:aria-selected="selectedPM === pm.id"
:aria-controls="`pm-panel-${pm.id}`"
:tabindex="selectedPM === pm.id ? 0 : -1"
type="button"
class="pm-tab px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-solid focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5 hover:text-fg"
@click="selectedPM = pm.id"
>
<span class="inline-block h-3 w-3" :class="pm.icon" aria-hidden="true" />
{{ pm.label }}
</button>
</div>
</template>
<style>
/*
* Package manager tab styling based on data-pm attribute on <html>.
* Selected tab gets highlighted background and border.
*/
[data-pm-tab] {
--pm-tab-bg: transparent;
--pm-tab-border: transparent;
--pm-tab-shadow: none;
background: var(--pm-tab-bg);
border-color: var(--pm-tab-border);
box-shadow: var(--pm-tab-shadow);
color: var(--fg-subtle);
}
:root[data-pm='npm'] [data-pm-tab='npm'],
:root[data-pm='pnpm'] [data-pm-tab='pnpm'],
:root[data-pm='yarn'] [data-pm-tab='yarn'],
:root[data-pm='bun'] [data-pm-tab='bun'],
:root[data-pm='deno'] [data-pm-tab='deno'],
:root[data-pm='vlt'] [data-pm-tab='vlt'] {
--pm-tab-bg: var(--bg);
--pm-tab-border: var(--border);
--pm-tab-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
color: var(--fg);
}
/* Fallback: when no data-pm is set, npm is selected by default */
:root:not([data-pm]) [data-pm-tab='npm'] {
--pm-tab-bg: var(--bg);
--pm-tab-border: var(--border);
--pm-tab-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
color: var(--fg);
}
</style>