Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/components/Input/Base.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ defineExpose({
v-bind="props.noCorrect ? noCorrect : undefined"
@focus="emit('focus', $event)"
@blur="emit('blur', $event)"
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)"
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)"
:class="{
'text-xs leading-[1.2] px-2 py-2 rounded-md': size === 'small',
'text-sm leading-none px-3 py-2.5 rounded-lg': size === 'medium',
'text-base leading-none px-6 py-3.5 h-14 rounded-xl': size === 'large',
'text-base leading-[1.4] px-6 py-4 rounded-xl': size === 'large',
}"
:disabled="
/** Catching Vue render-bug of invalid `disabled=false` attribute in the final HTML */
Expand Down
89 changes: 48 additions & 41 deletions app/components/Org/MembersPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const isLoadingTeams = shallowRef(false)
// Search/filter
const searchQuery = shallowRef('')
const filterRole = shallowRef<MemberRoleFilter>('all')
const filterTeam = shallowRef<string | null>(null)
const filterTeam = shallowRef<string>('')
const sortBy = shallowRef<'name' | 'role'>('name')
const sortOrder = shallowRef<'asc' | 'desc'>('asc')

Expand Down Expand Up @@ -362,18 +362,19 @@ watch(lastExecutionTime, () => {
</div>
<!-- Team filter -->
<div v-if="teamNames.length > 0">
<label for="team-filter" class="sr-only">{{ $t('org.members.filter_by_team') }}</label>
<select
<SelectField
:label="$t('org.members.filter_by_team')"
hidden-label
id="team-filter"
v-model="filterTeam"
name="team-filter"
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"
>
<option :value="null">{{ $t('org.members.all_teams') }}</option>
<option v-for="team in teamNames" :key="team" :value="team">
{{ team }}
</option>
</select>
block
size="sm"
:items="[
{ label: $t('org.members.all_teams'), value: '' },
...teamNames.map(team => ({ label: team, value: team })),
]"
/>
</div>
<div
class="flex items-center gap-1 text-xs"
Expand Down Expand Up @@ -462,22 +463,22 @@ watch(lastExecutionTime, () => {
<label :for="`role-${member.name}`" class="sr-only">{{
$t('org.members.change_role_for', { name: member.name })
}}</label>
<select
<SelectField
:label="$t('org.members.change_role_for', { name: member.name })"
hidden-label
:id="`role-${member.name}`"
:value="member.role"
:model-value="member.role"
:name="`role-${member.name}`"
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"
@change="
handleChangeRole(
member.name,
($event.target as HTMLSelectElement).value as 'developer' | 'admin' | 'owner',
)
"
>
<option value="developer">{{ getRoleLabel('developer') }}</option>
<option value="admin">{{ getRoleLabel('admin') }}</option>
<option value="owner">{{ getRoleLabel('owner') }}</option>
</select>
block
size="sm"
:items="[
{ label: getRoleLabel('developer'), value: 'developer' },
{ label: getRoleLabel('admin'), value: 'admin' },
{ label: getRoleLabel('owner'), value: 'owner' },
]"
:value="member.role"
@update:modelValue="value => handleChangeRole(member.name, value as MemberRole)"
/>
<!-- Remove button -->
<button
type="button"
Expand Down Expand Up @@ -528,30 +529,36 @@ watch(lastExecutionTime, () => {
size="small"
/>
<div class="flex items-center gap-2">
<label for="new-member-role" class="sr-only">{{ $t('org.members.role_label') }}</label>
<select
<SelectField
:label="$t('org.members.role_label')"
hidden-label
id="new-member-role"
v-model="newRole"
name="new-member-role"
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"
>
<option value="developer">{{ $t('org.members.role.developer') }}</option>
<option value="admin">{{ $t('org.members.role.admin') }}</option>
<option value="owner">{{ $t('org.members.role.owner') }}</option>
</select>
block
class="flex-1"
size="sm"
:items="[
{ label: $t('org.members.role.developer'), value: 'developer' },
{ label: $t('org.members.role.admin'), value: 'admin' },
{ label: $t('org.members.role.owner'), value: 'owner' },
]"
/>
<!-- Team selection -->
<label for="new-member-team" class="sr-only">{{ $t('org.members.team_label') }}</label>
<select
<SelectField
:label="$t('org.members.team_label')"
hidden-label
id="new-member-team"
v-model="newTeam"
name="new-member-team"
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"
>
<option value="">{{ $t('org.members.no_team') }}</option>
<option v-for="team in teamNames" :key="team" :value="team">
{{ team }}
</option>
</select>
block
class="flex-1"
size="sm"
:items="[
{ label: $t('org.members.no_team'), value: '' },
...teamNames.map(team => ({ label: team, value: team })),
]"
/>
<button
type="submit"
:disabled="!newUsername.trim() || isAddingMember"
Expand Down
52 changes: 25 additions & 27 deletions app/components/Package/AccessControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -243,41 +243,39 @@ watch(
<div v-if="showGrantAccess">
<form class="space-y-2" @submit.prevent="handleGrantAccess">
<div class="flex items-center gap-2">
<label for="grant-team-select" class="sr-only">{{
$t('package.access.select_team_label')
}}</label>
<select
<SelectField
:label="$t('package.access.select_team_label')"
hidden-label
id="grant-team-select"
v-model="selectedTeam"
name="grant-team"
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"
block
size="sm"
:disabled="isLoadingTeams"
>
<option value="" disabled>
{{
isLoadingTeams
:items="[
{
label: isLoadingTeams
? $t('package.access.loading_teams')
: $t('package.access.select_team')
}}
</option>
<option v-for="team in teams" :key="team" :value="team">
{{ orgName }}:{{ team }}
</option>
</select>
</div>
<div class="flex items-center gap-2">
<label for="grant-permission-select" class="sr-only">{{
$t('package.access.permission_label')
}}</label>
<select
: $t('package.access.select_team'),
value: '',
disabled: true,
},
...teams.map(team => ({ label: `${orgName}:${team}`, value: team })),
]"
/>
<SelectField
:label="$t('package.access.permission_label')"
hidden-label
id="grant-permission-select"
v-model="permission"
name="grant-permission"
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"
>
<option value="read-only">{{ $t('package.access.permission.read_only') }}</option>
<option value="read-write">{{ $t('package.access.permission.read_write') }}</option>
</select>
block
size="sm"
:items="[
{ label: $t('package.access.permission.read_only'), value: 'read-only' },
{ label: $t('package.access.permission.read_write'), value: 'read-write' },
]"
/>
<button
type="submit"
:disabled="!selectedTeam || isGranting"
Expand Down
8 changes: 4 additions & 4 deletions app/components/Package/Dependencies.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ const numberFormatter = useNumberFormatter()
{{ dep }}
</LinkBase>
<span class="flex items-center gap-1 max-w-[40%]" dir="ltr">
<span
<TooltipApp
v-if="outdatedDeps[dep]"
class="shrink-0"
class="shrink-0 p-2 -m-2"
:class="getVersionClass(outdatedDeps[dep])"
:title="getOutdatedTooltip(outdatedDeps[dep], $t)"
aria-hidden="true"
:text="getOutdatedTooltip(outdatedDeps[dep], $t)"
>
<span class="i-carbon:warning-alt w-3 h-3" />
</span>
</TooltipApp>
<LinkBase
v-if="getVulnerableDepInfo(dep)"
:to="packageRoute(dep, getVulnerableDepInfo(dep)!.version)"
Expand Down
48 changes: 14 additions & 34 deletions app/components/Package/DownloadAnalytics.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1474,44 +1474,24 @@ const chartConfig = computed(() => {
<div class="w-full relative" id="download-analytics" :aria-busy="pending ? 'true' : 'false'">
<div class="w-full mb-4 flex flex-col gap-3">
<div class="flex flex-col sm:flex-row gap-3 sm:gap-2 sm:items-end">
<div class="flex flex-col gap-1 sm:shrink-0">
<label
for="granularity"
class="text-3xs font-mono text-fg-subtle tracking-wide uppercase"
>
{{ $t('package.trends.granularity') }}
</label>

<div
class="flex items-center bg-bg-subtle border border-border rounded-md overflow-hidden"
>
<select
id="granularity"
v-model="selectedGranularity"
:disabled="pending"
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"
>
<option value="daily">
{{ $t('package.trends.granularity_daily') }}
</option>
<option value="weekly">
{{ $t('package.trends.granularity_weekly') }}
</option>
<option value="monthly">
{{ $t('package.trends.granularity_monthly') }}
</option>
<option value="yearly">
{{ $t('package.trends.granularity_yearly') }}
</option>
</select>
</div>
</div>
<SelectField
:label="$t('package.trends.granularity')"
id="granularity"
v-model="selectedGranularity"
:disabled="pending"
:items="[
{ label: $t('package.trends.granularity_daily'), value: 'daily' },
{ label: $t('package.trends.granularity_weekly'), value: 'weekly' },
{ label: $t('package.trends.granularity_monthly'), value: 'monthly' },
{ label: $t('package.trends.granularity_yearly'), value: 'yearly' },
]"
/>

<div class="grid grid-cols-2 gap-2 flex-1">
<div class="flex flex-col gap-1">
<label
for="startDate"
class="text-3xs font-mono text-fg-subtle tracking-wide uppercase"
class="text-2xs font-mono text-fg-subtle tracking-wide uppercase"
>
{{ $t('package.trends.start_date') }}
</label>
Expand All @@ -1532,7 +1512,7 @@ const chartConfig = computed(() => {
</div>

<div class="flex flex-col gap-1">
<label for="endDate" class="text-3xs font-mono text-fg-subtle tracking-wide uppercase">
<label for="endDate" class="text-2xs font-mono text-fg-subtle tracking-wide uppercase">
{{ $t('package.trends.end_date') }}
</label>
<div class="relative flex items-center">
Expand Down
8 changes: 4 additions & 4 deletions app/components/Package/InstallScripts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,18 @@ const isExpanded = shallowRef(false)
{{ dep }}
</LinkBase>
<span class="flex items-center gap-1">
<span
<TooltipApp
v-if="
outdatedNpxDeps[dep] &&
outdatedNpxDeps[dep].resolved !== outdatedNpxDeps[dep].latest
"
class="shrink-0"
class="shrink-0 p-2 -m-2"
:class="getVersionClass(outdatedNpxDeps[dep])"
:title="getOutdatedTooltip(outdatedNpxDeps[dep], $t)"
aria-hidden="true"
:text="getOutdatedTooltip(outdatedNpxDeps[dep], $t)"
>
<span class="i-carbon:warning-alt w-3 h-3" />
</span>
</TooltipApp>
<span
class="font-mono text-xs text-end truncate"
:class="getVersionClass(outdatedNpxDeps[dep])"
Expand Down
28 changes: 8 additions & 20 deletions app/components/Package/ListControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,26 +73,14 @@ const showFilteredCount = computed(() => {
</div>

<!-- Sort select -->
<div class="relative shrink-0 flex">
<label for="package-sort" class="sr-only">{{ $t('package.list.sort_label') }}</label>
<div class="relative">
<select
id="package-sort"
v-model="sortValue"
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"
>
<option v-for="option in sortOptions" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
<div
class="absolute inset-ie-3 top-1/2 -translate-y-1/2 text-fg-subtle pointer-events-none"
aria-hidden="true"
>
<div class="i-carbon:chevron-down w-4 h-4" />
</div>
</div>
</div>
<SelectField
:label="$t('package.list.sort_label')"
hidden-label
id="package-sort"
class="relative shrink-0"
v-model="sortValue"
:items="sortOptions.map(option => ({ label: option.label, value: option.value }))"
/>
</div>

<!-- Filtered count indicator -->
Expand Down
Loading
Loading