Skip to content

Commit 1a26c25

Browse files
refactor: move settings to a page (#225)
1 parent 88e5b89 commit 1a26c25

6 files changed

Lines changed: 163 additions & 211 deletions

File tree

app/components/AccentColorPicker.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { accentColors, selectedAccentColor, setAccentColor } = useAccentColor()
55
</script>
66

77
<template>
8-
<div role="listbox" aria-label="Accent colors" class="flex items-center justify-between">
8+
<div role="listbox" aria-label="Accent colors" class="flex items-center gap-4">
99
<button
1010
v-for="color in accentColors"
1111
:key="color.id"

app/components/AppHeader.vue

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ withDefaults(
1111
)
1212
1313
const { isConnected, npmUser } = useConnector()
14+
15+
const router = useRouter()
16+
onKeyStroke(',', e => {
17+
// Don't trigger if user is typing in an input
18+
const target = e.target as HTMLElement
19+
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
20+
return
21+
}
22+
23+
e.preventDefault()
24+
router.push('/settings')
25+
})
1426
</script>
1527

1628
<template>
@@ -64,9 +76,19 @@ const { isConnected, npmUser } = useConnector()
6476

6577
<!-- Right: User status + GitHub -->
6678
<div class="flex-shrink-0 flex items-center gap-6">
67-
<ClientOnly>
68-
<SettingsMenu />
69-
</ClientOnly>
79+
<NuxtLink
80+
to="/settings"
81+
class="link-subtle font-mono text-sm inline-flex items-center gap-2"
82+
aria-keyshortcuts=","
83+
>
84+
{{ $t('nav.settings') }}
85+
<kbd
86+
class="hidden sm:inline-flex items-center justify-center w-5 h-5 text-xs bg-bg-muted border border-border rounded"
87+
aria-hidden="true"
88+
>
89+
,
90+
</kbd>
91+
</NuxtLink>
7092

7193
<div v-if="showConnector">
7294
<ConnectorStatus />

app/components/SettingsMenu.vue

Lines changed: 0 additions & 205 deletions
This file was deleted.

app/pages/settings.vue

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<script setup lang="ts">
2+
const { settings } = useSettings()
3+
const { locale, locales, setLocale } = useI18n()
4+
const colorMode = useColorMode()
5+
6+
const availableLocales = computed(() =>
7+
locales.value.map(l => (typeof l === 'string' ? { code: l, name: l } : l)),
8+
)
9+
10+
useSeoMeta({
11+
title: 'Settings - npmx',
12+
})
13+
</script>
14+
15+
<template>
16+
<main class="container py-8 sm:py-12 w-full">
17+
<div class="space-y-1 p-4 rounded-lg bg-bg-muted border border-border">
18+
<button
19+
type="button"
20+
class="w-full flex items-center justify-between gap-3 px-2 py-2 rounded-md hover:bg-bg-muted transition-[background-color] duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
21+
role="menuitemcheckbox"
22+
:aria-checked="settings.relativeDates"
23+
@click="settings.relativeDates = !settings.relativeDates"
24+
>
25+
<span class="text-sm text-fg select-none">{{ $t('settings.relative_dates') }}</span>
26+
<span
27+
class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border-2 border-transparent transition-[background-color] duration-200 ease-in-out motion-reduce:transition-none shadow"
28+
:class="settings.relativeDates ? 'bg-fg' : 'bg-bg'"
29+
aria-hidden="true"
30+
>
31+
<span
32+
class="pointer-events-none inline-block h-4 w-4 rounded-full shadow-sm ring-0 transition-transform duration-200 ease-in-out motion-reduce:transition-none"
33+
:class="
34+
settings.relativeDates ? 'translate-x-4 bg-bg-subtle' : 'translate-x-0 bg-fg-muted'
35+
"
36+
/>
37+
</span>
38+
</button>
39+
40+
<!-- Include @types in install toggle -->
41+
<button
42+
type="button"
43+
class="w-full flex items-center justify-between gap-3 px-2 py-2 rounded-md hover:bg-bg-muted transition-[background-color] duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
44+
role="menuitemcheckbox"
45+
:aria-checked="settings.includeTypesInInstall"
46+
@click="settings.includeTypesInInstall = !settings.includeTypesInInstall"
47+
>
48+
<span class="text-sm text-fg select-none text-left">{{
49+
$t('settings.include_types')
50+
}}</span>
51+
<span
52+
class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border-2 border-transparent transition-[background-color] duration-200 ease-in-out motion-reduce:transition-none border border-border shadow"
53+
:class="settings.includeTypesInInstall ? 'bg-fg' : 'bg-bg'"
54+
aria-hidden="true"
55+
>
56+
<span
57+
class="pointer-events-none inline-block h-4 w-4 rounded-full shadow-sm ring-0 transition-transform duration-200 ease-in-out motion-reduce:transition-none"
58+
:class="
59+
settings.includeTypesInInstall
60+
? 'translate-x-4 bg-bg-subtle'
61+
: 'translate-x-0 bg-fg-muted'
62+
"
63+
/>
64+
</span>
65+
</button>
66+
67+
<!-- Theme selector -->
68+
<div class="pt-2 mt-2 border-t border-border">
69+
<div class="px-2 py-1">
70+
<label for="theme-select" class="text-xs text-fg-subtle uppercase tracking-wider">
71+
{{ $t('settings.theme') }}
72+
</label>
73+
</div>
74+
<div class="px-2 py-1">
75+
<select
76+
id="theme-select"
77+
:value="colorMode.preference"
78+
class="w-full bg-bg-muted border border-border rounded-md px-2 py-1.5 text-sm text-fg focus:outline-none focus:ring-2 focus:ring-fg/50 cursor-pointer"
79+
@change="
80+
colorMode.preference = ($event.target as HTMLSelectElement).value as
81+
| 'light'
82+
| 'dark'
83+
| 'system'
84+
"
85+
>
86+
<option value="system">{{ $t('settings.theme_system') }}</option>
87+
<option value="light">{{ $t('settings.theme_light') }}</option>
88+
<option value="dark">{{ $t('settings.theme_dark') }}</option>
89+
</select>
90+
</div>
91+
</div>
92+
93+
<!-- Language selector -->
94+
<div class="pt-2 mt-2 border-t border-border">
95+
<div class="px-2 py-1">
96+
<label for="language-select" class="text-xs text-fg-subtle uppercase tracking-wider">
97+
{{ $t('settings.language') }}
98+
</label>
99+
</div>
100+
<div class="px-2 py-1">
101+
<select
102+
id="language-select"
103+
:value="locale"
104+
class="w-full bg-bg-muted border border-border rounded-md px-2 py-1.5 text-sm text-fg focus:outline-none focus:ring-2 focus:ring-fg/50 cursor-pointer"
105+
@change="setLocale(($event.target as HTMLSelectElement).value as typeof locale)"
106+
>
107+
<option v-for="loc in availableLocales" :key="loc.code" :value="loc.code">
108+
{{ loc.name }}
109+
</option>
110+
</select>
111+
</div>
112+
<a
113+
href="https://github.com/npmx-dev/npmx.dev/tree/main/i18n/locales"
114+
target="_blank"
115+
rel="noopener noreferrer"
116+
class="flex items-center gap-1.5 px-2 py-1.5 text-xs text-fg-muted hover:text-fg transition-colors"
117+
>
118+
<span class="i-carbon-translate w-3.5 h-3.5" aria-hidden="true" />
119+
{{ $t('settings.help_translate') }}
120+
</a>
121+
</div>
122+
123+
<div class="pt-2 mt-2 border-t border-border">
124+
<h2 class="text-xs text-fg-subtle uppercase tracking-wider px-2 py-1">
125+
{{ $t('settings.accent_colors') }}
126+
</h2>
127+
<div class="px-2 py-2">
128+
<AccentColorPicker />
129+
</div>
130+
</div>
131+
</div>
132+
</main>
133+
</template>

0 commit comments

Comments
 (0)