Skip to content

Commit 6b6cc0f

Browse files
committed
feat: add select-base and select-field
1 parent 5c4d1fc commit 6b6cc0f

File tree

10 files changed

+276
-204
lines changed

10 files changed

+276
-204
lines changed

app/components/Input/Base.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ defineExpose({
3535
v-bind="props.noCorrect ? noCorrect : undefined"
3636
@focus="emit('focus', $event)"
3737
@blur="emit('blur', $event)"
38-
class="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)"
38+
class="appearance-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)"
3939
:class="{
4040
'text-xs leading-[1.2] px-2 py-2 rounded-md': size === 'small',
4141
'text-sm leading-none px-3 py-2.5 rounded-lg': size === 'medium',
42-
'text-base leading-none px-6 py-3.5 h-14 rounded-xl': size === 'large',
42+
'text-base leading-[1.4] px-6 py-4 rounded-xl': size === 'large',
4343
}"
4444
:disabled="
4545
/** Catching Vue render-bug of invalid `disabled=false` attribute in the final HTML */

app/components/Org/MembersPanel.vue

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const isLoadingTeams = shallowRef(false)
3535
// Search/filter
3636
const searchQuery = shallowRef('')
3737
const filterRole = shallowRef<MemberRoleFilter>('all')
38-
const filterTeam = shallowRef<string | null>(null)
38+
const filterTeam = shallowRef<string>('')
3939
const sortBy = shallowRef<'name' | 'role'>('name')
4040
const sortOrder = shallowRef<'asc' | 'desc'>('asc')
4141
@@ -362,18 +362,19 @@ watch(lastExecutionTime, () => {
362362
</div>
363363
<!-- Team filter -->
364364
<div v-if="teamNames.length > 0">
365-
<label for="team-filter" class="sr-only">{{ $t('org.members.filter_by_team') }}</label>
366-
<select
365+
<SelectField
366+
:label="$t('org.members.filter_by_team')"
367+
hidden-label
367368
id="team-filter"
368369
v-model="filterTeam"
369370
name="team-filter"
370-
class="px-2 py-1 font-mono text-xs bg-bg-subtle border border-border rounded text-fg transition-colors duration-200 focus:border-border-hover"
371-
>
372-
<option :value="null">{{ $t('org.members.all_teams') }}</option>
373-
<option v-for="team in teamNames" :key="team" :value="team">
374-
{{ team }}
375-
</option>
376-
</select>
371+
block
372+
size="sm"
373+
:items="[
374+
{ label: $t('org.members.all_teams'), value: '' },
375+
...teamNames.map(team => ({ label: team, value: team })),
376+
]"
377+
/>
377378
</div>
378379
<div
379380
class="flex items-center gap-1 text-xs"
@@ -462,22 +463,27 @@ watch(lastExecutionTime, () => {
462463
<label :for="`role-${member.name}`" class="sr-only">{{
463464
$t('org.members.change_role_for', { name: member.name })
464465
}}</label>
465-
<select
466+
<SelectField
467+
:label="$t('org.members.change_role_for', { name: member.name })"
468+
hidden-label
466469
:id="`role-${member.name}`"
467-
:value="member.role"
470+
v-model="member.role"
468471
:name="`role-${member.name}`"
469-
class="px-1.5 py-0.5 font-mono text-xs bg-bg-subtle border border-border rounded text-fg transition-colors duration-200 focus:border-border-hover"
472+
block
473+
size="sm"
474+
:items="[
475+
{ label: getRoleLabel('developer'), value: 'developer' },
476+
{ label: getRoleLabel('admin'), value: 'admin' },
477+
{ label: getRoleLabel('owner'), value: 'owner' },
478+
]"
479+
:value="member.role"
470480
@change="
471481
handleChangeRole(
472482
member.name,
473483
($event.target as HTMLSelectElement).value as 'developer' | 'admin' | 'owner',
474484
)
475485
"
476-
>
477-
<option value="developer">{{ getRoleLabel('developer') }}</option>
478-
<option value="admin">{{ getRoleLabel('admin') }}</option>
479-
<option value="owner">{{ getRoleLabel('owner') }}</option>
480-
</select>
486+
/>
481487
<!-- Remove button -->
482488
<button
483489
type="button"
@@ -528,30 +534,36 @@ watch(lastExecutionTime, () => {
528534
size="small"
529535
/>
530536
<div class="flex items-center gap-2">
531-
<label for="new-member-role" class="sr-only">{{ $t('org.members.role_label') }}</label>
532-
<select
537+
<SelectField
538+
:label="$t('org.members.role_label')"
539+
hidden-label
533540
id="new-member-role"
534541
v-model="newRole"
535542
name="new-member-role"
536-
class="flex-1 px-2 py-1.5 font-mono text-sm bg-bg border border-border rounded text-fg transition-colors duration-200 focus:border-border-hover"
537-
>
538-
<option value="developer">{{ $t('org.members.role.developer') }}</option>
539-
<option value="admin">{{ $t('org.members.role.admin') }}</option>
540-
<option value="owner">{{ $t('org.members.role.owner') }}</option>
541-
</select>
543+
block
544+
class="flex-1"
545+
size="sm"
546+
:items="[
547+
{ label: $t('org.members.role.developer'), value: 'developer' },
548+
{ label: $t('org.members.role.admin'), value: 'admin' },
549+
{ label: $t('org.members.role.owner'), value: 'owner' },
550+
]"
551+
/>
542552
<!-- Team selection -->
543-
<label for="new-member-team" class="sr-only">{{ $t('org.members.team_label') }}</label>
544-
<select
553+
<SelectField
554+
:label="$t('org.members.team_label')"
555+
hidden-label
545556
id="new-member-team"
546557
v-model="newTeam"
547558
name="new-member-team"
548-
class="flex-1 px-2 py-1.5 font-mono text-sm bg-bg border border-border rounded text-fg transition-colors duration-200 focus:border-border-hover"
549-
>
550-
<option value="">{{ $t('org.members.no_team') }}</option>
551-
<option v-for="team in teamNames" :key="team" :value="team">
552-
{{ team }}
553-
</option>
554-
</select>
559+
block
560+
class="flex-1"
561+
size="sm"
562+
:items="[
563+
{ label: $t('org.members.no_team'), value: '' },
564+
...teamNames.map(team => ({ label: team, value: team })),
565+
]"
566+
/>
555567
<button
556568
type="submit"
557569
:disabled="!newUsername.trim() || isAddingMember"

app/components/Package/AccessControls.vue

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -243,41 +243,39 @@ watch(
243243
<div v-if="showGrantAccess">
244244
<form class="space-y-2" @submit.prevent="handleGrantAccess">
245245
<div class="flex items-center gap-2">
246-
<label for="grant-team-select" class="sr-only">{{
247-
$t('package.access.select_team_label')
248-
}}</label>
249-
<select
246+
<SelectField
247+
:label="$t('package.access.select_team_label')"
248+
hidden-label
250249
id="grant-team-select"
251250
v-model="selectedTeam"
252251
name="grant-team"
253-
class="flex-1 px-2 py-1.5 font-mono text-sm bg-bg-subtle border border-border rounded text-fg transition-colors duration-200 focus:border-border-hover"
252+
block
253+
size="sm"
254254
:disabled="isLoadingTeams"
255-
>
256-
<option value="" disabled>
257-
{{
258-
isLoadingTeams
255+
:items="[
256+
{
257+
label: isLoadingTeams
259258
? $t('package.access.loading_teams')
260-
: $t('package.access.select_team')
261-
}}
262-
</option>
263-
<option v-for="team in teams" :key="team" :value="team">
264-
{{ orgName }}:{{ team }}
265-
</option>
266-
</select>
267-
</div>
268-
<div class="flex items-center gap-2">
269-
<label for="grant-permission-select" class="sr-only">{{
270-
$t('package.access.permission_label')
271-
}}</label>
272-
<select
259+
: $t('package.access.select_team'),
260+
value: '',
261+
disabled: true,
262+
},
263+
...teams.map(team => ({ label: `${orgName}:${team}`, value: team })),
264+
]"
265+
/>
266+
<SelectField
267+
:label="$t('package.access.permission_label')"
268+
hidden-label
273269
id="grant-permission-select"
274270
v-model="permission"
275271
name="grant-permission"
276-
class="flex-1 px-2 py-1.5 font-mono text-sm bg-bg-subtle border border-border rounded text-fg transition-colors duration-200 focus:border-border-hover"
277-
>
278-
<option value="read-only">{{ $t('package.access.permission.read_only') }}</option>
279-
<option value="read-write">{{ $t('package.access.permission.read_write') }}</option>
280-
</select>
272+
block
273+
size="sm"
274+
:items="[
275+
{ label: $t('package.access.permission.read_only'), value: 'read-only' },
276+
{ label: $t('package.access.permission.read_write'), value: 'read-write' },
277+
]"
278+
/>
281279
<button
282280
type="submit"
283281
:disabled="!selectedTeam || isGranting"

app/components/Package/DownloadAnalytics.vue

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,44 +1474,24 @@ const chartConfig = computed(() => {
14741474
<div class="w-full relative" id="download-analytics" :aria-busy="pending ? 'true' : 'false'">
14751475
<div class="w-full mb-4 flex flex-col gap-3">
14761476
<div class="flex flex-col sm:flex-row gap-3 sm:gap-2 sm:items-end">
1477-
<div class="flex flex-col gap-1 sm:shrink-0">
1478-
<label
1479-
for="granularity"
1480-
class="text-3xs font-mono text-fg-subtle tracking-wide uppercase"
1481-
>
1482-
{{ $t('package.trends.granularity') }}
1483-
</label>
1484-
1485-
<div
1486-
class="flex items-center bg-bg-subtle border border-border rounded-md overflow-hidden"
1487-
>
1488-
<select
1489-
id="granularity"
1490-
v-model="selectedGranularity"
1491-
:disabled="pending"
1492-
class="w-full px-4 py-3 leading-none bg-bg-subtle font-mono text-sm text-fg outline-none appearance-none focus-visible:outline-accent/70"
1493-
>
1494-
<option value="daily">
1495-
{{ $t('package.trends.granularity_daily') }}
1496-
</option>
1497-
<option value="weekly">
1498-
{{ $t('package.trends.granularity_weekly') }}
1499-
</option>
1500-
<option value="monthly">
1501-
{{ $t('package.trends.granularity_monthly') }}
1502-
</option>
1503-
<option value="yearly">
1504-
{{ $t('package.trends.granularity_yearly') }}
1505-
</option>
1506-
</select>
1507-
</div>
1508-
</div>
1477+
<SelectField
1478+
:label="$t('package.trends.granularity')"
1479+
id="granularity"
1480+
v-model="selectedGranularity"
1481+
:disabled="pending"
1482+
:items="[
1483+
{ label: $t('package.trends.granularity_daily'), value: 'daily' },
1484+
{ label: $t('package.trends.granularity_weekly'), value: 'weekly' },
1485+
{ label: $t('package.trends.granularity_monthly'), value: 'monthly' },
1486+
{ label: $t('package.trends.granularity_yearly'), value: 'yearly' },
1487+
]"
1488+
/>
15091489

15101490
<div class="grid grid-cols-2 gap-2 flex-1">
15111491
<div class="flex flex-col gap-1">
15121492
<label
15131493
for="startDate"
1514-
class="text-3xs font-mono text-fg-subtle tracking-wide uppercase"
1494+
class="text-2xs font-mono text-fg-subtle tracking-wide uppercase"
15151495
>
15161496
{{ $t('package.trends.start_date') }}
15171497
</label>
@@ -1532,7 +1512,7 @@ const chartConfig = computed(() => {
15321512
</div>
15331513

15341514
<div class="flex flex-col gap-1">
1535-
<label for="endDate" class="text-3xs font-mono text-fg-subtle tracking-wide uppercase">
1515+
<label for="endDate" class="text-2xs font-mono text-fg-subtle tracking-wide uppercase">
15361516
{{ $t('package.trends.end_date') }}
15371517
</label>
15381518
<div class="relative flex items-center">

app/components/Package/ListControls.vue

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,14 @@ const showFilteredCount = computed(() => {
7373
</div>
7474

7575
<!-- Sort select -->
76-
<div class="relative shrink-0 flex">
77-
<label for="package-sort" class="sr-only">{{ $t('package.list.sort_label') }}</label>
78-
<div class="relative">
79-
<select
80-
id="package-sort"
81-
v-model="sortValue"
82-
class="appearance-none bg-bg-subtle border border-border rounded-lg ps-3 pe-8 py-3 leading-none font-mono text-sm text-fg transition-colors duration-200 focus:(border-border-hover outline-none) hover:border-border-hover"
83-
>
84-
<option v-for="option in sortOptions" :key="option.value" :value="option.value">
85-
{{ option.label }}
86-
</option>
87-
</select>
88-
<div
89-
class="absolute inset-ie-3 top-1/2 -translate-y-1/2 text-fg-subtle pointer-events-none"
90-
aria-hidden="true"
91-
>
92-
<div class="i-carbon:chevron-down w-4 h-4" />
93-
</div>
94-
</div>
95-
</div>
76+
<SelectField
77+
:label="$t('package.list.sort_label')"
78+
hidden-label
79+
id="package-sort"
80+
class="relative shrink-0"
81+
v-model="sortValue"
82+
:items="sortOptions.map(option => ({ label: option.label, value: option.value }))"
83+
/>
9684
</div>
9785

9886
<!-- Filtered count indicator -->

app/components/Package/ListToolbar.vue

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -162,30 +162,20 @@ function getSortKeyLabelKey(key: SortKey): string {
162162
<!-- Sort controls -->
163163
<div class="flex items-center gap-1 shrink-0 order-1 sm:order-1">
164164
<!-- Sort key dropdown -->
165-
<div class="relative">
166-
<label for="sort-select" class="sr-only">{{ $t('filters.sort.label') }}</label>
167-
<select
168-
id="sort-select"
169-
:value="currentSort.key"
170-
class="appearance-none bg-bg-subtle border border-border rounded-md ps-3 pe-8 py-1.5 font-mono text-sm text-fg transition-colors duration-200 hover:border-border-hover"
171-
@change="handleSortKeyChange"
172-
>
173-
<option
174-
v-for="keyConfig in availableSortKeys"
175-
:key="keyConfig.key"
176-
:value="keyConfig.key"
177-
:disabled="keyConfig.disabled"
178-
>
179-
{{ getSortKeyLabelKey(keyConfig.key) }}
180-
</option>
181-
</select>
182-
<div
183-
class="flex items-center absolute inset-ie-2 top-1/2 -translate-y-1/2 text-fg-subtle pointer-events-none"
184-
aria-hidden="true"
185-
>
186-
<span class="i-carbon-chevron-down w-4 h-4" />
187-
</div>
188-
</div>
165+
<SelectField
166+
:label="$t('filters.sort.label')"
167+
hidden-label
168+
id="sort-select"
169+
v-model="currentSort.key"
170+
@change="handleSortKeyChange"
171+
:items="
172+
availableSortKeys.map(keyConfig => ({
173+
label: getSortKeyLabelKey(keyConfig.key),
174+
value: keyConfig.key,
175+
disabled: keyConfig.disabled,
176+
}))
177+
"
178+
/>
189179

190180
<!-- Sort direction toggle -->
191181
<button

0 commit comments

Comments
 (0)