Skip to content

Commit aad32c3

Browse files
committed
feat: add and use input component
1 parent 9867a47 commit aad32c3

File tree

15 files changed

+144
-81
lines changed

15 files changed

+144
-81
lines changed

app/components/AppHeader.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,12 @@ onKeyStroke(
122122

123123
<!-- Center: Search bar + nav items -->
124124
<div
125-
class="flex-1 flex items-center justify-center md:gap-6"
126-
:class="{ 'hidden sm:flex': !isSearchExpanded }"
125+
class="flex-1 flex items-center md:gap-6"
126+
:class="{
127+
'hidden sm:flex': !isSearchExpanded,
128+
'justify-end': isOnHomePage,
129+
'justify-center': !isOnHomePage,
130+
}"
127131
>
128132
<!-- Search bar (hidden on mobile unless expanded) -->
129133
<HeaderSearchBox

app/components/Compare/PackageSelector.vue

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,16 @@ function handleBlur() {
128128

129129
<!-- Add package input -->
130130
<div v-if="packages.length < maxPackages" class="relative">
131-
<div class="relative group">
131+
<div class="relative group flex items-center">
132132
<label for="package-search" class="sr-only">
133133
{{ $t('compare.selector.search_label') }}
134134
</label>
135135
<span
136-
class="absolute inset-y-0 start-3 flex items-center text-fg-subtle pointer-events-none group-focus-within:text-accent"
137-
aria-hidden="true"
136+
class="absolute inset-is-3 text-fg-subtle font-mono text-md pointer-events-none transition-colors duration-200 motion-reduce:transition-none [.group:hover:not(:focus-within)_&]:text-fg/80 group-focus-within:text-accent z-1"
138137
>
139-
<span class="i-carbon:search w-4 h-4" />
138+
/
140139
</span>
141-
<input
140+
<InputBase
142141
id="package-search"
143142
v-model="inputValue"
144143
type="text"
@@ -147,7 +146,9 @@ function handleBlur() {
147146
? $t('compare.selector.search_first')
148147
: $t('compare.selector.search_add')
149148
"
150-
class="w-full bg-bg-subtle border border-border rounded-lg ps-10 pe-4 py-2.5 font-mono text-sm text-fg placeholder:text-fg-subtle motion-reduce:transition-none duration-200 focus:border-accent focus-visible:(outline-2 outline-accent/70)"
149+
noCorrect
150+
size="medium"
151+
class="w-full min-w-25 ps-7"
151152
aria-autocomplete="list"
152153
@focus="isInputFocused = true"
153154
@blur="handleBlur"

app/components/Filter/Panel.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,15 @@ const hasActiveFilters = computed(() => !!filterSummary.value)
222222
</button>
223223
</div>
224224
</div>
225-
<input
225+
<InputBase
226226
id="filter-search"
227227
type="text"
228228
:value="filters.text"
229229
:placeholder="searchPlaceholder"
230230
autocomplete="off"
231-
class="w-full bg-bg-subtle border border-border rounded-md px-4 py-3 font-mono text-sm text-fg placeholder:text-fg-subtle transition-[border-color,outline-color] duration-300 hover:border-fg-subtle outline-2 outline-transparent focus:border-accent focus-visible:(outline-2 outline-accent/70)"
231+
class="w-full min-w-25"
232+
size="medium"
233+
noCorrect
232234
@input="handleTextInput"
233235
/>
234236
</div>

app/components/Header/AuthModal.client.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,15 @@ watch(handleInput, newHandleInput => {
7272
>
7373
{{ $t('auth.modal.handle_label') }}
7474
</label>
75-
<input
75+
<InputBase
7676
id="handle-input"
7777
v-model="handleInput"
7878
type="text"
7979
name="handle"
8080
:placeholder="$t('auth.modal.handle_placeholder')"
81-
v-bind="noCorrect"
82-
class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 hover:border-fg-subtle focus:border-accent focus-visible:(outline-2 outline-accent/70)"
81+
noCorrect
82+
class="w-full"
83+
size="medium"
8384
/>
8485
</div>
8586

app/components/Header/ConnectorModal.vue

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,15 @@ function handleDisconnect() {
161161
>
162162
{{ $t('connector.modal.token_label') }}
163163
</label>
164-
<input
164+
<InputBase
165165
id="connector-token"
166166
v-model="tokenInput"
167167
type="password"
168168
name="connector-token"
169169
:placeholder="$t('connector.modal.token_placeholder')"
170-
v-bind="noCorrect"
171-
class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 hover:border-fg-subtle focus-visible:outline-none focus-visible:ring-2 focus-visible:outline-accent/70"
170+
noCorrect
171+
class="w-full"
172+
size="medium"
172173
/>
173174
</div>
174175

@@ -185,14 +186,15 @@ function handleDisconnect() {
185186
>
186187
{{ $t('connector.modal.port_label') }}
187188
</label>
188-
<input
189+
<InputBase
189190
id="connector-port"
190191
v-model="portInput"
191192
type="text"
192193
name="connector-port"
193194
inputmode="numeric"
194195
autocomplete="off"
195-
class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg transition-colors duration-200 hover:border-fg-subtle focus:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:outline-accent/70"
196+
class="w-full"
197+
size="medium"
196198
/>
197199
</div>
198200
</details>

app/components/Header/SearchBox.vue

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,6 @@ watch(
7070
},
7171
)
7272
73-
function handleSearchBlur() {
74-
isSearchFocused.value = false
75-
emit('blur')
76-
}
77-
function handleSearchFocus() {
78-
isSearchFocused.value = true
79-
emit('focus')
80-
}
81-
8273
function handleSubmit() {
8374
if (pagesWithLocalFilter.has(route.name as string)) {
8475
router.push({
@@ -114,17 +105,15 @@ defineExpose({ focus })
114105
/
115106
</span>
116107

117-
<input
108+
<InputBase
118109
id="header-search"
119110
ref="inputRef"
120111
v-model="searchQuery"
121112
type="search"
122113
name="q"
123114
:placeholder="$t('search.placeholder')"
124-
v-bind="noCorrect"
125-
class="w-full min-w-25 bg-bg-subtle border border-border rounded-md ps-7 pe-3 py-1.5 font-mono text-sm text-fg placeholder:text-fg-subtle transition-[border-color,outline-color] duration-300 hover:border-fg-subtle outline-2 outline-transparent focus:border-accent focus-visible:(outline-2 outline-accent/70)"
126-
@focus="handleSearchFocus"
127-
@blur="handleSearchBlur"
115+
noCorrect
116+
class="w-full min-w-25 ps-7"
128117
/>
129118
<button type="submit" class="sr-only">{{ $t('search.button') }}</button>
130119
</div>

app/components/Input/Base.vue

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<script setup lang="ts">
2+
import { noCorrect } from '~/utils/input'
3+
4+
const props = withDefaults(
5+
defineProps<{
6+
modelValue?: string
7+
size?: 'small' | 'medium' | 'large'
8+
noCorrect?: boolean
9+
}>(),
10+
{
11+
modelValue: '',
12+
size: 'medium',
13+
noCorrect: true,
14+
},
15+
)
16+
17+
const emit = defineEmits<{
18+
'update:modelValue': [value: string]
19+
'focus': [event: FocusEvent]
20+
'blur': [event: FocusEvent]
21+
}>()
22+
23+
const el = useTemplateRef('el')
24+
25+
const model = computed({
26+
get: () => props.modelValue ?? '',
27+
set: (value: string) => emit('update:modelValue', value),
28+
})
29+
30+
defineExpose({
31+
focus: () => el.value?.focus(),
32+
blur: () => el.value?.blur(),
33+
getBoundingClientRect: () => el.value?.getBoundingClientRect(),
34+
})
35+
</script>
36+
37+
<template>
38+
<input
39+
ref="el"
40+
v-model="model"
41+
v-bind="props.noCorrect ? noCorrect : undefined"
42+
@focus="emit('focus', $event)"
43+
@blur="emit('blur', $event)"
44+
class="w-full leading-none bg-bg-subtle border border-border font-mono text-fg placeholder:text-fg-subtle transition-[border-color,outline-color] duration-300 hover:border-fg-subtle outline-2 outline-transparent outline-offset-2 focus:border-accent focus-visible:outline-accent/70 disabled:(opacity-50 cursor-not-allowed)"
45+
:class="{
46+
'text-xs px-2 py-1.25 h-8 rounded-md': size === 'small',
47+
'text-sm px-3 py-2.5 h-10 rounded-lg': size === 'medium',
48+
'text-base px-6 py-3.5 h-14 rounded-xl': size === 'large',
49+
}"
50+
/>
51+
</template>

app/components/Org/MembersPanel.vue

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -331,14 +331,15 @@ watch(lastExecutionTime, () => {
331331
aria-hidden="true"
332332
/>
333333
<label for="members-search" class="sr-only">{{ $t('org.members.filter_label') }}</label>
334-
<input
334+
<InputBase
335335
id="members-search"
336336
v-model="searchQuery"
337337
type="search"
338338
name="members-search"
339339
:placeholder="$t('org.members.filter_placeholder')"
340-
v-bind="noCorrect"
341-
class="w-full ps-7 pe-2 py-1.5 font-mono text-sm bg-bg-subtle border border-border rounded text-fg placeholder:text-fg-subtle transition-colors duration-200 focus:border-accent focus-visible:(outline-2 outline-accent/70)"
340+
noCorrect
341+
class="w-full min-w-25 ps-7"
342+
size="small"
342343
/>
343344
</div>
344345
<div
@@ -516,14 +517,15 @@ watch(lastExecutionTime, () => {
516517
<label for="new-member-username" class="sr-only">{{
517518
$t('org.members.username_label')
518519
}}</label>
519-
<input
520+
<InputBase
520521
id="new-member-username"
521522
v-model="newUsername"
522523
type="text"
523524
name="new-member-username"
524525
:placeholder="$t('org.members.username_placeholder')"
525-
v-bind="noCorrect"
526-
class="w-full px-2 py-1.5 font-mono text-sm bg-bg border border-border rounded text-fg placeholder:text-fg-subtle transition-colors duration-200 focus:border-border-hover focus-visible:outline-accent/70"
526+
noCorrect
527+
class="w-full min-w-25"
528+
size="small"
527529
/>
528530
<div class="flex items-center gap-2">
529531
<label for="new-member-role" class="sr-only">{{ $t('org.members.role_label') }}</label>
@@ -553,7 +555,7 @@ watch(lastExecutionTime, () => {
553555
<button
554556
type="submit"
555557
:disabled="!newUsername.trim() || isAddingMember"
556-
class="px-3 py-1.5 font-mono text-xs text-bg bg-fg rounded transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70"
558+
class="px-3 py-2 font-mono text-xs text-bg bg-fg rounded transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70"
557559
>
558560
{{ isAddingMember ? '…' : $t('org.members.add_button') }}
559561
</button>

app/components/Org/OperationsQueue.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ watch(isExecuting, executing => {
242242
</div>
243243
<form class="flex items-center gap-2" @submit.prevent="handleRetryWithOtp">
244244
<label for="otp-input" class="sr-only">{{ $t('operations.queue.otp_label') }}</label>
245-
<input
245+
<InputBase
246246
id="otp-input"
247247
v-model="otpInput"
248248
type="text"
@@ -252,12 +252,13 @@ watch(isExecuting, executing => {
252252
:placeholder="$t('operations.queue.otp_placeholder')"
253253
autocomplete="one-time-code"
254254
spellcheck="false"
255-
class="flex-1 px-3 py-1.5 font-mono text-sm bg-bg border border-border rounded text-fg placeholder:text-fg-subtle transition-colors duration-200 focus:border-border-hover focus-visible:outline-accent/70"
255+
class="flex-1 min-w-25"
256+
size="small"
256257
/>
257258
<button
258259
type="submit"
259260
:disabled="!otpInput.trim() || isExecuting"
260-
class="px-3 py-1.5 font-mono text-xs text-bg bg-amber-500 rounded transition-all duration-200 hover:bg-amber-400 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50"
261+
class="px-3 py-2 font-mono text-xs text-bg bg-amber-500 rounded transition-all duration-200 hover:bg-amber-400 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50"
261262
>
262263
{{ isExecuting ? $t('operations.queue.retrying') : $t('operations.queue.retry_otp') }}
263264
</button>

app/components/Org/TeamsPanel.vue

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,15 @@ watch(lastExecutionTime, () => {
287287
aria-hidden="true"
288288
/>
289289
<label for="teams-search" class="sr-only">{{ $t('org.teams.filter_label') }}</label>
290-
<input
290+
<InputBase
291291
id="teams-search"
292292
v-model="searchQuery"
293293
type="search"
294294
name="teams-search"
295295
:placeholder="$t('org.teams.filter_placeholder')"
296-
v-bind="noCorrect"
297-
class="w-full ps-7 pe-2 py-1.5 font-mono text-sm bg-bg-subtle border border-border rounded text-fg placeholder:text-fg-subtle transition-colors duration-200 focus:border-accent focus-visible:(outline-2 outline-accent/70)"
296+
noCorrect
297+
class="w-full min-w-25 ps-7"
298+
size="medium"
298299
/>
299300
</div>
300301
<div
@@ -425,7 +426,7 @@ watch(lastExecutionTime, () => {
425426
>
426427
~{{ user }}
427428
</NuxtLink>
428-
<span class="font-mono text-sm text-fg">{{ teamName }}</span>
429+
<span class="font-mono text-sm text-fg mx-2">{{ teamName }}</span>
429430
<button
430431
type="button"
431432
class="p-1 text-fg-subtle hover:text-red-400 transition-colors duration-200 rounded focus-visible:outline-accent/70"
@@ -446,14 +447,15 @@ watch(lastExecutionTime, () => {
446447
<label :for="`add-user-${teamName}`" class="sr-only">{{
447448
$t('org.teams.username_to_add', { team: teamName })
448449
}}</label>
449-
<input
450+
<InputBase
450451
:id="`add-user-${teamName}`"
451452
v-model="newUserUsername"
452453
type="text"
453454
:name="`add-user-${teamName}`"
454455
:placeholder="$t('org.teams.username_placeholder')"
455-
v-bind="noCorrect"
456-
class="flex-1 px-2 py-1 font-mono text-sm bg-bg-subtle border border-border rounded text-fg placeholder:text-fg-subtle transition-colors duration-200 focus:border-border-hover focus-visible:outline-accent/70"
456+
noCorrect
457+
class="flex-1 min-w-25"
458+
size="medium"
457459
/>
458460
<button
459461
type="submit"
@@ -497,25 +499,26 @@ watch(lastExecutionTime, () => {
497499
<form class="flex items-center gap-2" @submit.prevent="handleCreateTeam">
498500
<div class="flex-1 flex items-center">
499501
<span
500-
class="px-2 py-1.5 font-mono text-sm text-fg-subtle bg-bg border border-ie-0 border-border rounded-is"
502+
class="px-2 py-3 leading-none font-mono text-sm text-fg-subtle bg-bg border border-ie-0 border-border rounded-is"
501503
>
502504
{{ orgName }}:
503505
</span>
504506
<label for="new-team-name" class="sr-only">{{ $t('org.teams.team_name_label') }}</label>
505-
<input
507+
<InputBase
506508
id="new-team-name"
507509
v-model="newTeamName"
508510
type="text"
509511
name="new-team-name"
510512
:placeholder="$t('org.teams.team_name_placeholder')"
511-
v-bind="noCorrect"
512-
class="flex-1 px-2 py-1.5 font-mono text-sm bg-bg border border-border rounded-ie text-fg placeholder:text-fg-subtle transition-colors duration-200 focus:border-border-hover focus-visible:outline-accent/70"
513+
noCorrect
514+
class="flex-1 min-w-25 rounded-l-none"
515+
size="medium"
513516
/>
514517
</div>
515518
<button
516519
type="submit"
517520
:disabled="!newTeamName.trim() || isCreatingTeam"
518-
class="px-3 py-1.5 font-mono text-xs text-bg bg-fg rounded transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70"
521+
class="px-3 py-2 font-mono text-xs text-bg bg-fg rounded transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70"
519522
>
520523
{{ isCreatingTeam ? '…' : $t('org.teams.create_button') }}
521524
</button>

0 commit comments

Comments
 (0)