Merge remote-tracking branch 'origin/main' into prospector/disclosures-frontend

This commit is contained in:
Prospector
2026-08-07 18:12:40 -07:00
77 changed files with 3592 additions and 3498 deletions
@@ -312,7 +312,7 @@ defineExpose({ open: openMenu, close: closeMenu })
v-if="isOpen"
:id="menuId"
ref="panelElement"
class="fixed isolate z-[9999] flex min-w-48 flex-col gap-1 rounded-[14px] bg-surface-3 p-2 shadow-lg ring-1 ring-surface-5"
class="fixed isolate z-[9999] rounded-[14px] bg-surface-3 shadow-lg ring-1 ring-surface-5"
:style="[panelStyle, { transformOrigin: menuTransformOrigin }]"
role="menu"
:aria-label="props.label"
@@ -326,76 +326,85 @@ defineExpose({ open: openMenu, close: closeMenu })
:data-side="resolvedSide"
:style="anchorStyle"
/>
<template v-for="(option, index) in visibleOptions" :key="option.id ?? `divider-${index}`">
<div v-if="isDivider(option)" role="separator" class="my-1 h-px bg-surface-5" />
<RouterLink
v-else-if="isLink(option) && option.to !== undefined && !option.disabled"
v-tooltip="option.tooltip"
:to="option.to"
:class="menuItemClasses"
:style="getMenuItemStyle(option)"
:data-tone="option.tone && option.tone !== 'default' ? option.tone : undefined"
:data-hover-filled="option.hoverFilled || option.hoverFilledOnly || undefined"
:data-hover-filled-only="option.hoverFilledOnly || undefined"
role="menuitem"
tabindex="-1"
@click="handleLink(option, $event)"
@keydown="handleLinkKeydown(option, $event)"
@focus="selectedIndex = getMenuItems().indexOf($event.currentTarget as HTMLElement)"
<div
data-anchored-scroll-region
class="flex min-w-48 flex-col gap-1 overflow-y-auto p-2"
:style="{ maxHeight: panelStyle.maxHeight }"
>
<template
v-for="(option, index) in visibleOptions"
:key="option.id ?? `divider-${index}`"
>
<slot :name="option.id" :option="option">
<component :is="option.icon" v-if="option.icon" aria-hidden="true" />
{{ option.label }}
</slot>
</RouterLink>
<div v-if="isDivider(option)" role="separator" class="my-1 h-px bg-surface-5" />
<a
v-else-if="isLink(option)"
v-tooltip="option.tooltip"
:href="option.disabled ? undefined : option.href"
:target="option.target"
:rel="option.rel ?? (option.target === '_blank' ? 'noopener noreferrer' : undefined)"
:download="option.download"
:aria-disabled="option.disabled || undefined"
:class="menuItemClasses"
:style="getMenuItemStyle(option)"
:data-tone="option.tone && option.tone !== 'default' ? option.tone : undefined"
:data-hover-filled="option.hoverFilled || option.hoverFilledOnly || undefined"
:data-hover-filled-only="option.hoverFilledOnly || undefined"
role="menuitem"
tabindex="-1"
@click="handleLink(option, $event)"
@keydown="handleLinkKeydown(option, $event)"
@focus="selectedIndex = getMenuItems().indexOf($event.currentTarget as HTMLElement)"
>
<slot :name="option.id" :option="option">
<component :is="option.icon" v-if="option.icon" aria-hidden="true" />
{{ option.label }}
</slot>
</a>
<RouterLink
v-else-if="isLink(option) && option.to !== undefined && !option.disabled"
v-tooltip="option.tooltip"
:to="option.to"
:class="menuItemClasses"
:style="getMenuItemStyle(option)"
:data-tone="option.tone && option.tone !== 'default' ? option.tone : undefined"
:data-hover-filled="option.hoverFilled || option.hoverFilledOnly || undefined"
:data-hover-filled-only="option.hoverFilledOnly || undefined"
role="menuitem"
tabindex="-1"
@click="handleLink(option, $event)"
@keydown="handleLinkKeydown(option, $event)"
@focus="selectedIndex = getMenuItems().indexOf($event.currentTarget as HTMLElement)"
>
<slot :name="option.id" :option="option">
<component :is="option.icon" v-if="option.icon" aria-hidden="true" />
{{ option.label }}
</slot>
</RouterLink>
<button
v-else
v-tooltip="option.tooltip"
type="button"
:aria-disabled="option.disabled || undefined"
:class="menuItemClasses"
:style="getMenuItemStyle(option)"
:data-tone="option.tone && option.tone !== 'default' ? option.tone : undefined"
:data-hover-filled="option.hoverFilled || option.hoverFilledOnly || undefined"
:data-hover-filled-only="option.hoverFilledOnly || undefined"
role="menuitem"
tabindex="-1"
@click="handleAction(option, $event)"
@focus="selectedIndex = getMenuItems().indexOf($event.currentTarget as HTMLElement)"
>
<slot :name="option.id" :option="option">
<component :is="option.icon" v-if="option.icon" aria-hidden="true" />
{{ option.label }}
</slot>
</button>
</template>
<a
v-else-if="isLink(option)"
v-tooltip="option.tooltip"
:href="option.disabled ? undefined : option.href"
:target="option.target"
:rel="option.rel ?? (option.target === '_blank' ? 'noopener noreferrer' : undefined)"
:download="option.download"
:aria-disabled="option.disabled || undefined"
:class="menuItemClasses"
:style="getMenuItemStyle(option)"
:data-tone="option.tone && option.tone !== 'default' ? option.tone : undefined"
:data-hover-filled="option.hoverFilled || option.hoverFilledOnly || undefined"
:data-hover-filled-only="option.hoverFilledOnly || undefined"
role="menuitem"
tabindex="-1"
@click="handleLink(option, $event)"
@keydown="handleLinkKeydown(option, $event)"
@focus="selectedIndex = getMenuItems().indexOf($event.currentTarget as HTMLElement)"
>
<slot :name="option.id" :option="option">
<component :is="option.icon" v-if="option.icon" aria-hidden="true" />
{{ option.label }}
</slot>
</a>
<button
v-else
v-tooltip="option.tooltip"
type="button"
:aria-disabled="option.disabled || undefined"
:class="menuItemClasses"
:style="getMenuItemStyle(option)"
:data-tone="option.tone && option.tone !== 'default' ? option.tone : undefined"
:data-hover-filled="option.hoverFilled || option.hoverFilledOnly || undefined"
:data-hover-filled-only="option.hoverFilledOnly || undefined"
role="menuitem"
tabindex="-1"
@click="handleAction(option, $event)"
@focus="selectedIndex = getMenuItems().indexOf($event.currentTarget as HTMLElement)"
>
<slot :name="option.id" :option="option">
<component :is="option.icon" v-if="option.icon" aria-hidden="true" />
{{ option.label }}
</slot>
</button>
</template>
</div>
</div>
</Transition>
</Teleport>
@@ -128,7 +128,7 @@ defineExpose({ open: openMenu, close: closeMenu })
v-if="isOpen"
:id="panelId"
ref="panelElement"
class="fixed isolate z-[9999] rounded-[14px] bg-surface-3 p-4 text-primary shadow-lg ring-1 ring-surface-5"
class="fixed isolate z-[9999] overflow-y-auto rounded-[14px] bg-surface-3 p-4 text-primary shadow-lg ring-1 ring-surface-5"
:style="panelStyle"
:role="props.panelRole"
:aria-label="props.label"
@@ -474,7 +474,6 @@ import {
import {
type GameVersionTag,
getVersionGroupsForDisplay,
type Version,
type VersionDisplayGroup,
} from '@modrinth/utils'
import { Menu } from 'floating-vue'
@@ -500,15 +499,12 @@ const formatBytes = useFormatBytes()
const MAX_GAME_VERSION_TAGS = 5
const MAX_PLATFORM_TAGS = 3
type VersionWithDisplayUrlEnding = Version & {
type VersionWithDisplayUrlEnding = Labrinth.Versions.v3.Version & {
displayUrlEnding: string
environment?: Labrinth.Projects.v3.Environment
mrpack_loaders?: string[]
}
type DisplayVersion = VersionWithDisplayUrlEnding & {
noModLoader: boolean
files_missing_attribution?: boolean
}
type VersionTableColumn =
@@ -536,7 +532,7 @@ const props = withDefaults(
currentMember?: boolean
loaders: Labrinth.Tags.v2.Loader[]
gameVersions: GameVersionTag[]
versionLink?: (version: Version) => string
versionLink?: (version: Labrinth.Versions.v3.Version) => string
openModal?: () => void
createVersionButtonSecondary?: boolean
}>(),
@@ -0,0 +1,28 @@
<template>
<AutoLink
:to="`/project/${project.slug}`"
:target="newTab ? '_blank' : undefined"
class="group inline-flex min-w-0 max-w-full items-center gap-1"
:style="{ color: `var(--color-${getProjectStatusColor(project.status)})` }"
>
<component :is="getProjectStatusIcon(project.status)" class="size-4 shrink-0" />
<span class="min-w-0 truncate font-medium group-hover:underline">{{ project.name }}</span>
</AutoLink>
</template>
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { getProjectStatusColor, getProjectStatusIcon } from '../../utils'
import AutoLink from '../base/AutoLink.vue'
withDefaults(
defineProps<{
project: Pick<Labrinth.Projects.v3.Project, 'name' | 'slug' | 'status'>
newTab?: boolean
}>(),
{
newTab: false,
},
)
</script>
@@ -17,4 +17,5 @@ export { default as ProjectSidebarLinks } from './ProjectSidebarLinks.vue'
export { default as ProjectSidebarServerInfo } from './ProjectSidebarServerInfo.vue'
export { default as ProjectSidebarTags } from './ProjectSidebarTags.vue'
export { default as ProjectStatusBadge } from './ProjectStatusBadge.vue'
export { default as ProjectStatusLink } from './ProjectStatusLink.vue'
export { default as TagsOverflow } from './TagsOverflow.vue'
@@ -120,16 +120,17 @@
</template>
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { ChevronLeftIcon, FilterIcon, XCircleIcon, XIcon } from '@modrinth/assets'
import type { MultiSelectOption } from '@modrinth/ui'
import { Checkbox, formatLoader, FormattedTag, MultiSelect, TagItem, useVIntl } from '@modrinth/ui'
import type { GameVersionTag, Version } from '@modrinth/utils'
import type { GameVersionTag } from '@modrinth/utils'
import { computed, ref } from 'vue'
import type { LocationQueryValue } from 'vue-router'
import { useRoute } from 'vue-router'
const props = defineProps<{
versions: Version[]
versions: Labrinth.Versions.v3.Version[]
gameVersions: GameVersionTag[]
baseId?: string
}>()