mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 12:05:53 +00:00
feat: implement selecting compatible versions when there are multiple
This commit is contained in:
@@ -1,7 +1,25 @@
|
||||
<template>
|
||||
<div
|
||||
class="grid grid-cols-[1fr_min-content] items-center gap-3 rounded-2xl bg-surface-2 px-3 py-3"
|
||||
:role="selectable ? 'radio' : undefined"
|
||||
:aria-checked="selectable ? selected : undefined"
|
||||
:tabindex="selectable ? 0 : undefined"
|
||||
class="grid items-center gap-3 rounded-2xl border border-solid px-3 py-3 transition-all"
|
||||
:class="{
|
||||
'grid-cols-[min-content_minmax(0,1fr)_min-content]': selectable,
|
||||
'grid-cols-[minmax(0,1fr)_min-content]': !selectable,
|
||||
'cursor-pointer border-brand bg-surface-4 text-contrast': selectable && selected,
|
||||
'cursor-pointer border-surface-5 bg-surface-4 hover:brightness-[115%]':
|
||||
selectable && !selected,
|
||||
'border-transparent bg-surface-2': !selectable,
|
||||
}"
|
||||
@click="select"
|
||||
@keydown.enter.self.prevent="select"
|
||||
@keydown.space.self.prevent="select"
|
||||
>
|
||||
<template v-if="selectable">
|
||||
<RadioButtonCheckedIcon v-if="selected" aria-hidden="true" class="size-5 text-brand" />
|
||||
<RadioButtonIcon v-else aria-hidden="true" class="size-5 text-secondary" />
|
||||
</template>
|
||||
<div class="flex min-w-0 flex-col gap-1">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<nuxt-link
|
||||
@@ -21,13 +39,21 @@
|
||||
<span v-tooltip="publishedTooltip" class="min-w-0 truncate capitalize">
|
||||
{{ publishedLabel }}
|
||||
</span>
|
||||
<div v-if="primaryFile" class="h-1.5 w-1.5 flex-shrink-0 rounded-full bg-surface-5"></div>
|
||||
<div
|
||||
v-if="primaryFile"
|
||||
class="h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary opacity-30"
|
||||
></div>
|
||||
<span v-if="primaryFile" class="flex-shrink-0">
|
||||
{{ primaryFileSizeLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<ButtonStyled v-if="primaryFile" :color="color" :type="type" :circular="circular">
|
||||
<ButtonStyled
|
||||
v-if="primaryFile && showDownload"
|
||||
:color="color"
|
||||
:type="type"
|
||||
:circular="circular"
|
||||
>
|
||||
<a
|
||||
v-tooltip="circular ? formatMessage(messages.download) : null"
|
||||
:href="primaryFileDownloadUrl"
|
||||
@@ -50,7 +76,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { DownloadIcon } from '@modrinth/assets'
|
||||
import { DownloadIcon, RadioButtonCheckedIcon, RadioButtonIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
type CdnDownloadReason,
|
||||
@@ -84,6 +110,9 @@ const props = withDefaults(
|
||||
color?: 'brand' | 'standard'
|
||||
type?: 'standard' | 'transparent'
|
||||
circular?: boolean
|
||||
selectable?: boolean
|
||||
selected?: boolean
|
||||
showDownload?: boolean
|
||||
}>(),
|
||||
{
|
||||
downloadReason: 'standalone',
|
||||
@@ -92,11 +121,15 @@ const props = withDefaults(
|
||||
color: 'brand',
|
||||
type: 'standard',
|
||||
circular: false,
|
||||
selectable: false,
|
||||
selected: false,
|
||||
showDownload: true,
|
||||
},
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
download: []
|
||||
select: []
|
||||
}>()
|
||||
|
||||
const { createProjectDownloadUrl } = useCdnDownloadContext()
|
||||
@@ -131,6 +164,11 @@ const primaryFileSizeLabel = computed(() => {
|
||||
return formatBytes(primaryFile.value.size)
|
||||
})
|
||||
|
||||
function select() {
|
||||
if (!props.selectable) return
|
||||
emit('select')
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
downloadVersion: {
|
||||
id: 'project.download.download-version',
|
||||
|
||||
@@ -80,35 +80,47 @@
|
||||
</Combobox>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedVersion && downloadDataLoaded" class="flex flex-col gap-2.5">
|
||||
<div
|
||||
v-if="selectedVersion && downloadDataLoaded"
|
||||
:role="compatibleVersions.length > 1 ? 'radiogroup' : undefined"
|
||||
:aria-label="
|
||||
compatibleVersions.length > 1 ? formatMessage(messages.compatibleVersionTitle) : undefined
|
||||
"
|
||||
class="flex flex-col gap-2.5"
|
||||
>
|
||||
<h3
|
||||
v-if="[...suggestedPreReleaseVersions, selectedVersion].length > 1"
|
||||
v-if="compatibleVersions.length > 1"
|
||||
class="relative top-0.5 m-0 text-base font-semibold text-contrast"
|
||||
>
|
||||
{{ formatMessage(messages.compatibleVersionTitle) }}
|
||||
</h3>
|
||||
<CompatibleVersionCard
|
||||
v-for="compatibleVersion in compatibleVersions"
|
||||
:key="compatibleVersion.id"
|
||||
:project="project"
|
||||
:version="selectedVersion"
|
||||
:version="compatibleVersion"
|
||||
:download-reason="downloadReason"
|
||||
:current-game-version="currentGameVersion"
|
||||
:current-platform="currentPlatform"
|
||||
:color="hasAdditionalDownloads ? 'standard' : 'brand'"
|
||||
:type="hasAdditionalDownloads ? 'transparent' : 'standard'"
|
||||
:circular="hasAdditionalDownloads"
|
||||
@download="emit('download')"
|
||||
/>
|
||||
<CompatibleVersionCard
|
||||
v-for="suggestedVersion in suggestedPreReleaseVersions"
|
||||
:key="suggestedVersion.version.id"
|
||||
:project="project"
|
||||
:version="suggestedVersion.version"
|
||||
:download-reason="downloadReason"
|
||||
:current-game-version="currentGameVersion"
|
||||
:current-platform="currentPlatform"
|
||||
color="standard"
|
||||
type="transparent"
|
||||
circular
|
||||
:selectable="compatibleVersions.length > 1"
|
||||
:selected="compatibleVersion.id === selectedVersion.id"
|
||||
:show-download="compatibleVersions.length === 1 || compatibleVersion.id === selectedVersion.id"
|
||||
:color="
|
||||
compatibleVersion.id === selectedVersion.id &&
|
||||
compatibleVersions.length === 1 &&
|
||||
!hasAdditionalDownloads
|
||||
? 'brand'
|
||||
: 'standard'
|
||||
"
|
||||
:type="
|
||||
compatibleVersion.id === selectedVersion.id &&
|
||||
compatibleVersions.length === 1 &&
|
||||
!hasAdditionalDownloads
|
||||
? 'standard'
|
||||
: 'transparent'
|
||||
"
|
||||
:circular="hasAdditionalDownloads || compatibleVersions.length > 1"
|
||||
@select="selectCompatibleVersion(compatibleVersion)"
|
||||
@download="emit('download')"
|
||||
/>
|
||||
</div>
|
||||
@@ -154,10 +166,6 @@ type DownloadableFile = {
|
||||
filename: string
|
||||
}
|
||||
|
||||
type SuggestedPreReleaseVersion = {
|
||||
version: Labrinth.Versions.v3.Version
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
project: DownloadModalProject
|
||||
@@ -196,6 +204,7 @@ const tags = useGeneratedState()
|
||||
|
||||
const userSelectedGameVersion = ref<string | null>(props.initialGameVersion)
|
||||
const userSelectedPlatform = ref<string | null>(props.initialPlatform)
|
||||
const userSelectedCompatibleVersionId = ref<string | null>(null)
|
||||
const showAllVersions = ref(defaultShowAllVersions())
|
||||
const versionFilter = ref('')
|
||||
|
||||
@@ -367,10 +376,42 @@ const filteredAlpha = computed<Labrinth.Versions.v3.Version | undefined>(() => {
|
||||
return latestVersionByType('alpha')
|
||||
})
|
||||
|
||||
const selectedVersion = computed<Labrinth.Versions.v3.Version | null>(() => {
|
||||
const defaultSelectedVersion = computed<Labrinth.Versions.v3.Version | null>(() => {
|
||||
return filteredRelease.value || filteredBeta.value || filteredAlpha.value || null
|
||||
})
|
||||
|
||||
const suggestedPreReleaseVersions = computed<Labrinth.Versions.v3.Version[]>(() => {
|
||||
if (!defaultSelectedVersion.value || defaultSelectedVersion.value.version_type !== 'release') return []
|
||||
|
||||
const versions: Labrinth.Versions.v3.Version[] = []
|
||||
const beta = filteredBeta.value
|
||||
if (beta && isNewerThan(beta, defaultSelectedVersion.value)) {
|
||||
versions.push(beta)
|
||||
}
|
||||
|
||||
const alpha = filteredAlpha.value
|
||||
if (alpha && isNewerThan(alpha, defaultSelectedVersion.value)) {
|
||||
versions.push(alpha)
|
||||
}
|
||||
|
||||
return versions
|
||||
})
|
||||
|
||||
const compatibleVersions = computed<Labrinth.Versions.v3.Version[]>(() => {
|
||||
if (!defaultSelectedVersion.value) return []
|
||||
return [defaultSelectedVersion.value, ...suggestedPreReleaseVersions.value]
|
||||
})
|
||||
|
||||
const selectedVersion = computed<Labrinth.Versions.v3.Version | null>(() => {
|
||||
return (
|
||||
compatibleVersions.value.find(
|
||||
(version) => version.id === userSelectedCompatibleVersionId.value,
|
||||
) ||
|
||||
defaultSelectedVersion.value ||
|
||||
null
|
||||
)
|
||||
})
|
||||
|
||||
const selectedPrimaryFile = computed<Labrinth.Versions.v3.VersionFile | null>(() => {
|
||||
return (
|
||||
selectedVersion.value?.files?.find((file) => file.primary) ||
|
||||
@@ -379,27 +420,6 @@ const selectedPrimaryFile = computed<Labrinth.Versions.v3.VersionFile | null>(()
|
||||
)
|
||||
})
|
||||
|
||||
const suggestedPreReleaseVersions = computed<SuggestedPreReleaseVersion[]>(() => {
|
||||
if (!selectedVersion.value || selectedVersion.value.version_type !== 'release') return []
|
||||
|
||||
const versions: SuggestedPreReleaseVersion[] = []
|
||||
const beta = filteredBeta.value
|
||||
if (beta && isNewerThan(beta, selectedVersion.value)) {
|
||||
versions.push({
|
||||
version: beta,
|
||||
})
|
||||
}
|
||||
|
||||
const alpha = filteredAlpha.value
|
||||
if (alpha && isNewerThan(alpha, selectedVersion.value)) {
|
||||
versions.push({
|
||||
version: alpha,
|
||||
})
|
||||
}
|
||||
|
||||
return versions
|
||||
})
|
||||
|
||||
const hasAdditionalDownloads = computed(() => {
|
||||
const hrefs = new Set<string>()
|
||||
|
||||
@@ -428,11 +448,16 @@ watch(
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch([currentGameVersion, currentPlatform], () => {
|
||||
userSelectedCompatibleVersionId.value = null
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.resetKey,
|
||||
() => {
|
||||
userSelectedGameVersion.value = props.initialGameVersion
|
||||
userSelectedPlatform.value = props.initialPlatform
|
||||
userSelectedCompatibleVersionId.value = null
|
||||
showAllVersions.value = defaultShowAllVersions()
|
||||
versionFilter.value = ''
|
||||
},
|
||||
@@ -451,6 +476,10 @@ function selectPlatform(platform?: string) {
|
||||
emit('selectPlatform', platform)
|
||||
}
|
||||
|
||||
function selectCompatibleVersion(version: Labrinth.Versions.v3.Version) {
|
||||
userSelectedCompatibleVersionId.value = version.id
|
||||
}
|
||||
|
||||
function latestVersionByType(type: Labrinth.Versions.v3.VersionChannel) {
|
||||
return filteredVersions.value
|
||||
.filter((version) => version.version_type === type)
|
||||
|
||||
Reference in New Issue
Block a user