Improve editing project versions (#4933)

* add edit versions dropdown menu

* implement improved edit version with individual edit stages

* make changelog bigger

* update button styles

* remove hover button when hover on row

* bring editing versions back to project settings

* bring back gallery edit and upload in project page

* fix progress value

* fix admonition import

* fix v3 upload for modpacks

* fix modpack loader display for editing version and better open edit/create modal handling

* fix currentMember prop

* fix modpack loader displaying incorrectly

* fix max length

* fix version url after making an edit to version and fix delete

* small max height fix

* hide edit dependencies for modpack

* pnpm run fix

* fix import

* add tooltip

* update icons

* update copy and create version button style
This commit is contained in:
Truman Gao
2025-12-19 13:24:14 -08:00
committed by GitHub
parent 0663b8adb0
commit 3f93041ca2
25 changed files with 586 additions and 164 deletions

View File

@@ -4,7 +4,12 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client' import type { Labrinth } from '@modrinth/api-client'
import { injectProjectPageContext, MultiStageModal } from '@modrinth/ui' import {
injectModrinthClient,
injectNotificationManager,
injectProjectPageContext,
MultiStageModal,
} from '@modrinth/ui'
import type { ComponentExposed } from 'vue-component-type-helpers' import type { ComponentExposed } from 'vue-component-type-helpers'
import { import {
@@ -20,15 +25,53 @@ provideManageVersionContext(ctx)
const { newDraftVersion, setProjectType } = ctx const { newDraftVersion, setProjectType } = ctx
const { projectV2 } = injectProjectPageContext() const { projectV2 } = injectProjectPageContext()
const { addNotification } = injectNotificationManager()
const { labrinth } = injectModrinthClient()
function showCreateVersionModal(version: Labrinth.Versions.v3.DraftVersion | null = null) { async function openEditVersionModal(versionId: string, projectId: string, stageId?: string | null) {
try {
const versionData = await labrinth.versions_v3.getVersion(versionId)
const draftVersionData: Labrinth.Versions.v3.DraftVersion = {
project_id: projectId,
version_id: versionId,
name: versionData.name ?? '',
version_number: versionData.version_number ?? '',
changelog: versionData.changelog ?? '',
game_versions: versionData.game_versions ?? [],
version_type: versionData.version_type ?? 'release',
loaders: versionData.loaders ?? [],
dependencies: versionData.dependencies ?? [],
existing_files: versionData.files ?? [],
environment: versionData.environment,
}
if (projectV2.value.project_type === 'modpack' && draftVersionData.loaders.includes('mrpack')) {
draftVersionData.loaders.push(...(versionData.mrpack_loaders ?? []))
}
openCreateVersionModal(draftVersionData, stageId)
} catch (err: any) {
addNotification({
title: 'An error occurred',
text: err.data ? err.data.description : err,
type: 'error',
})
}
}
function openCreateVersionModal(
version: Labrinth.Versions.v3.DraftVersion | null = null,
stageId: string | null = null,
) {
newDraftVersion(projectV2.value.id, version) newDraftVersion(projectV2.value.id, version)
setProjectType(projectV2.value) setProjectType(projectV2.value)
modal.value?.setStage(0) modal.value?.setStage(stageId ?? 0)
modal.value?.show() modal.value?.show()
} }
defineExpose({ defineExpose({
show: showCreateVersionModal, openEditVersionModal,
openCreateVersionModal,
}) })
</script> </script>

View File

@@ -1,9 +1,11 @@
<template> <template>
<MarkdownEditor <div class="w-full">
v-model="draftVersion.changelog" <MarkdownEditor
:on-image-upload="onImageUpload" v-model="draftVersion.changelog"
:max-height="500" :on-image-upload="onImageUpload"
/> :max-height="500"
/>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="flex w-full max-w-full flex-col gap-6"> <div class="flex w-full max-w-full flex-col gap-6 sm:w-[512px]">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<span class="font-semibold text-contrast">Add dependency</span> <span class="font-semibold text-contrast">Add dependency</span>
<div class="flex flex-col gap-3 rounded-2xl border border-solid border-surface-5 p-4"> <div class="flex flex-col gap-3 rounded-2xl border border-solid border-surface-5 p-4">

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="flex flex-col gap-6"> <div class="flex flex-col gap-6 sm:w-[512px]">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="font-semibold text-contrast"> <span class="font-semibold text-contrast">
Version type <span class="text-red">*</span> Version type <span class="text-red">*</span>
@@ -33,7 +33,7 @@
placeholder="Enter subtitle..." placeholder="Enter subtitle..."
type="text" type="text"
autocomplete="off" autocomplete="off"
maxlength="32" maxlength="256"
/> />
</div> </div>
@@ -102,7 +102,7 @@
</div> </div>
<div <div
class="flex flex-col gap-1.5 gap-y-4 rounded-xl border border-solid border-surface-5 p-3 py-4" class="flex max-h-56 flex-col gap-1.5 gap-y-4 overflow-y-auto rounded-xl border border-solid border-surface-5 p-3 py-4"
> >
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<TagItem <TagItem
@@ -176,13 +176,13 @@ const loaders = computed(() => generatedState.value.loaders)
const isModpack = computed(() => projectType.value === 'modpack') const isModpack = computed(() => projectType.value === 'modpack')
const editLoaders = () => { const editLoaders = () => {
modal.value?.setStage('edit-loaders') modal.value?.setStage('from-details-loaders')
} }
const editVersions = () => { const editVersions = () => {
modal.value?.setStage('edit-mc-versions') modal.value?.setStage('from-details-mc-versions')
} }
const editEnvironment = () => { const editEnvironment = () => {
modal.value?.setStage('edit-environment') modal.value?.setStage('from-details-environment')
} }
const usingDetectedVersions = computed(() => { const usingDetectedVersions = computed(() => {

View File

@@ -1,5 +1,7 @@
<template> <template>
<ProjectSettingsEnvSelector v-model="draftVersion.environment" /> <div class="sm:w-[512px]">
<ProjectSettingsEnvSelector v-model="draftVersion.environment" />
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="flex w-full flex-col gap-4"> <div class="flex w-full flex-col gap-4 sm:w-[512px]">
<template v-if="!(filesToAdd.length || draftVersion.existing_files?.length)"> <template v-if="!(filesToAdd.length || draftVersion.existing_files?.length)">
<DropzoneFileInput <DropzoneFileInput
aria-label="Upload file" aria-label="Upload file"

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="space-y-6"> <div class="space-y-6 sm:w-[512px]">
<LoaderPicker <LoaderPicker
v-model="draftVersion.loaders" v-model="draftVersion.loaders"
:loaders="generatedState.loaders" :loaders="generatedState.loaders"

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="flex flex-col gap-6"> <div class="flex flex-col gap-6 sm:w-[512px]">
<McVersionPicker v-model="draftVersion.game_versions" :game-versions="gameVersions" /> <McVersionPicker v-model="draftVersion.game_versions" :game-versions="gameVersions" />
<div v-if="draftVersion.game_versions.length" class="space-y-1"> <div v-if="draftVersion.game_versions.length" class="space-y-1">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">

View File

@@ -47,7 +47,7 @@
> >
</div> </div>
<a <a
:href="version.primaryFile.url" :href="version.primaryFile?.url"
class="iconified-button download" class="iconified-button download"
:title="`Download ${version.name}`" :title="`Download ${version.name}`"
> >

View File

@@ -196,7 +196,7 @@
</div> </div>
</div> </div>
<Admonition v-if="!hideGalleryAdmonition && currentMember" type="info" class="mb-4"> <Admonition v-if="!hideGalleryAdmonition && currentMember" type="info" class="mb-4">
Managing gallery has moved! You can now add and edit gallery images in the Creating and editing gallery images can now be done directly from the
<NuxtLink to="settings/gallery" class="font-medium text-blue hover:underline" <NuxtLink to="settings/gallery" class="font-medium text-blue hover:underline"
>project settings</NuxtLink >project settings</NuxtLink
>. >.
@@ -224,6 +224,27 @@
</div> </div>
</template> </template>
</Admonition> </Admonition>
<div v-if="currentMember && project.gallery.length" class="card header-buttons">
<FileInput
:max-size="5242880"
:accept="acceptFileTypes"
prompt="Upload an image"
aria-label="Upload an image"
class="iconified-button brand-button"
:disabled="!isPermission(currentMember?.permissions, 1 << 2)"
@change="handleFiles"
>
<UploadIcon aria-hidden="true" />
</FileInput>
<span class="indicator">
<InfoIcon aria-hidden="true" /> Click to choose an image or drag one onto this page
</span>
<DropArea
:accept="acceptFileTypes"
:disabled="!isPermission(currentMember?.permissions, 1 << 2)"
@change="handleFiles"
/>
</div>
<div v-if="project.gallery.length" class="items"> <div v-if="project.gallery.length" class="items">
<div v-for="(item, index) in project.gallery" :key="index" class="card gallery-item"> <div v-for="(item, index) in project.gallery" :key="index" class="card gallery-item">
<a class="gallery-thumbnail" @click="expandImage(item, index)"> <a class="gallery-thumbnail" @click="expandImage(item, index)">
@@ -247,6 +268,37 @@
<CalendarIcon aria-hidden="true" aria-label="Date created" /> <CalendarIcon aria-hidden="true" aria-label="Date created" />
{{ $dayjs(item.created).format('MMMM D, YYYY') }} {{ $dayjs(item.created).format('MMMM D, YYYY') }}
</div> </div>
<div v-if="currentMember" class="gallery-buttons input-group">
<button
class="iconified-button"
@click="
() => {
resetEdit()
editIndex = index
editTitle = item.title
editDescription = item.description
editFeatured = item.featured
editOrder = item.ordering
$refs.modal_edit_item.show()
}
"
>
<EditIcon aria-hidden="true" />
Edit
</button>
<button
class="iconified-button"
@click="
() => {
deleteIndex = index
$refs.modal_confirm.show()
}
"
>
<TrashIcon aria-hidden="true" />
Remove
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -266,9 +318,11 @@
import { import {
CalendarIcon, CalendarIcon,
ContractIcon, ContractIcon,
EditIcon,
ExpandIcon, ExpandIcon,
ExternalIcon, ExternalIcon,
ImageIcon, ImageIcon,
InfoIcon,
LeftArrowIcon, LeftArrowIcon,
PlusIcon, PlusIcon,
RightArrowIcon, RightArrowIcon,
@@ -276,18 +330,23 @@ import {
SettingsIcon, SettingsIcon,
StarIcon, StarIcon,
TransferIcon, TransferIcon,
TrashIcon,
UploadIcon,
XIcon, XIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { import {
Admonition, Admonition,
ButtonStyled, ButtonStyled,
ConfirmModal, ConfirmModal,
DropArea,
FileInput, FileInput,
injectNotificationManager, injectNotificationManager,
NewModal as Modal, NewModal as Modal,
} from '@modrinth/ui' } from '@modrinth/ui'
import { useLocalStorage } from '@vueuse/core' import { useLocalStorage } from '@vueuse/core'
import { isPermission } from '~/utils/permissions.ts'
const props = defineProps({ const props = defineProps({
project: { project: {
type: Object, type: Object,

View File

@@ -1,6 +1,9 @@
<template> <template>
<div> <div>
<CreateProjectVersionModal ref="modal"></CreateProjectVersionModal> <CreateProjectVersionModal
v-if="currentMember"
ref="create-project-version-modal"
></CreateProjectVersionModal>
<ConfirmModal <ConfirmModal
v-if="currentMember" v-if="currentMember"
@@ -27,32 +30,59 @@
project.slug ? project.slug : project.id project.slug ? project.slug : project.id
}/version/${encodeURI(version.displayUrlEnding)}` }/version/${encodeURI(version.displayUrlEnding)}`
" "
:open-modal="currentMember ? openModal : undefined" :open-modal="currentMember ? () => handleOpenCreateVersionModal() : undefined"
> >
<template #actions="{ version }"> <template #actions="{ version }">
<ButtonStyled circular type="transparent"> <ButtonStyled circular type="transparent">
<a <OverflowMenu
v-tooltip="`Download`" v-tooltip="'Edit version'"
:href="getPrimaryFile(version).url" class="hover:!bg-button-bg [&>svg]:!text-green"
class="group-hover:!bg-brand group-hover:[&>svg]:!text-brand-inverted" :dropdown-id="`${baseDropdownId}-edit-${version.id}`"
aria-label="Download" :options="[
@click="emit('onDownload')" {
> id: 'edit-details',
<DownloadIcon aria-hidden="true" /> action: () => handleOpenEditVersionModal(version.id, project.id, 'add-details'),
</a> },
</ButtonStyled> {
<ButtonStyled circular type="transparent"> id: 'edit-changelog',
<a action: () => handleOpenEditVersionModal(version.id, project.id, 'add-changelog'),
v-tooltip="`Edit version`" },
aria-label="Edit" {
@click="() => handleOpenEditVersionModal(version)" id: 'edit-dependencies',
action: () =>
handleOpenEditVersionModal(version.id, project.id, 'add-dependencies'),
shown: project.project_type !== 'modpack',
},
{
id: 'edit-files',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-files'),
},
]"
aria-label="Edit version"
> >
<EditIcon aria-hidden="true" /> <EditIcon aria-hidden="true" />
</a> <template #edit-files>
<FileIcon aria-hidden="true" />
Edit files
</template>
<template #edit-details>
<InfoIcon aria-hidden="true" />
Edit details
</template>
<template #edit-dependencies>
<BoxIcon aria-hidden="true" />
Edit dependencies
</template>
<template #edit-changelog>
<AlignLeftIcon aria-hidden="true" />
Edit changelog
</template>
</OverflowMenu>
</ButtonStyled> </ButtonStyled>
<ButtonStyled circular type="transparent"> <ButtonStyled circular type="transparent">
<OverflowMenu <OverflowMenu
class="group-hover:!bg-button-bg" v-tooltip="'More options'"
class="hover:!bg-button-bg"
:dropdown-id="`${baseDropdownId}-${version.id}`" :dropdown-id="`${baseDropdownId}-${version.id}`"
:options="[ :options="[
{ {
@@ -110,8 +140,24 @@
}, },
{ divider: true, shown: !!currentMember }, { divider: true, shown: !!currentMember },
{ {
id: 'edit', id: 'edit-details',
action: () => handleOpenEditVersionModal(version), action: () => handleOpenEditVersionModal(version.id, project.id, 'add-details'),
shown: !!currentMember,
},
{
id: 'edit-changelog',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-changelog'),
shown: !!currentMember,
},
{
id: 'edit-dependencies',
action: () =>
handleOpenEditVersionModal(version.id, project.id, 'add-dependencies'),
shown: !!currentMember && project.project_type !== 'modpack',
},
{
id: 'edit-files',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-files'),
shown: !!currentMember, shown: !!currentMember,
}, },
{ {
@@ -148,9 +194,21 @@
<ReportIcon aria-hidden="true" /> <ReportIcon aria-hidden="true" />
Report Report
</template> </template>
<template #edit> <template #edit-files>
<EditIcon aria-hidden="true" /> <FileIcon aria-hidden="true" />
Edit Edit files
</template>
<template #edit-details>
<InfoIcon aria-hidden="true" />
Edit details
</template>
<template #edit-dependencies>
<BoxIcon aria-hidden="true" />
Edit dependencies
</template>
<template #edit-changelog>
<AlignLeftIcon aria-hidden="true" />
Edit changelog
</template> </template>
<template #delete> <template #delete>
<TrashIcon aria-hidden="true" /> <TrashIcon aria-hidden="true" />
@@ -230,7 +288,9 @@
<div>Create your first project version.</div> <div>Create your first project version.</div>
<br /> <br />
<ButtonStyled color="green"> <ButtonStyled color="green">
<button @click="openModal"><PlusIcon /> Create version</button> <button @click="() => createProjectVersionModal?.openCreateVersionModal()">
<PlusIcon /> Create version
</button>
</ButtonStyled> </ButtonStyled>
</div> </div>
</div> </div>
@@ -241,10 +301,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Labrinth } from '@modrinth/api-client' import type { Labrinth } from '@modrinth/api-client'
import { import {
AlignLeftIcon,
BoxIcon,
ClipboardCopyIcon, ClipboardCopyIcon,
DownloadIcon, DownloadIcon,
EditIcon, EditIcon,
ExternalIcon, ExternalIcon,
FileIcon,
InfoIcon,
LinkIcon, LinkIcon,
MoreVerticalIcon, MoreVerticalIcon,
PlusIcon, PlusIcon,
@@ -261,6 +325,7 @@ import {
OverflowMenu, OverflowMenu,
ProjectPageVersions, ProjectPageVersions,
} from '@modrinth/ui' } from '@modrinth/ui'
import { useTemplateRef } from 'vue'
import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue' import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue'
import { reportVersion } from '~/utils/report-helpers.ts' import { reportVersion } from '~/utils/report-helpers.ts'
@@ -278,23 +343,32 @@ const client = injectModrinthClient()
const { addNotification } = injectNotificationManager() const { addNotification } = injectNotificationManager()
const { refreshVersions } = injectProjectPageContext() const { refreshVersions } = injectProjectPageContext()
const modal = ref<InstanceType<typeof CreateProjectVersionModal>>()
function openModal() {
modal.value?.show?.()
}
const tags = useGeneratedState() const tags = useGeneratedState()
const flags = useFeatureFlags() const flags = useFeatureFlags()
const auth = await useAuth() const auth = await useAuth()
const createProjectVersionModal = useTemplateRef('create-project-version-modal')
const deleteVersionModal = ref<InstanceType<typeof ConfirmModal>>() const deleteVersionModal = ref<InstanceType<typeof ConfirmModal>>()
const selectedVersion = ref<string | null>(null) const selectedVersion = ref<string | null>(null)
const handleOpenCreateVersionModal = () => {
if (!currentMember) return
createProjectVersionModal.value?.openCreateVersionModal()
}
const handleOpenEditVersionModal = (
versionId: string,
projectId: string,
stageId?: string | null,
) => {
if (!currentMember) return
createProjectVersionModal.value?.openEditVersionModal(versionId, projectId, stageId)
}
const versionsWithDisplayUrl = computed(() => const versionsWithDisplayUrl = computed(() =>
versions.value.map((v) => ({ versions.value.map((v) => ({
...v, ...v,
displayUrlEnding: v.version_number, displayUrlEnding: v.id,
})), })),
) )
@@ -337,30 +411,4 @@ async function deleteVersion() {
stopLoading() stopLoading()
} }
async function handleOpenEditVersionModal(version: Labrinth.Versions.v3.Version) {
selectedVersion.value = version.id
try {
const versionData = await client.labrinth.versions_v3.getVersion(version.id)
modal.value?.show({
project_id: project.id,
version_id: version.id,
name: versionData.name ?? '',
version_number: versionData.version_number ?? '',
changelog: versionData.changelog ?? '',
game_versions: versionData.game_versions ?? [],
version_type: versionData.version_type ?? 'release',
loaders: versionData.loaders ?? [],
dependencies: versionData.dependencies ?? [],
existing_files: versionData.files ?? [],
environment: versionData.environment,
})
} catch (err: any) {
addNotification({
title: 'An error occurred',
text: err.data ? err.data.description : err,
type: 'error',
})
}
}
</script> </script>

View File

@@ -1,7 +1,22 @@
<template> <template>
<section class="experimental-styles-within overflow-visible"> <section class="experimental-styles-within overflow-visible">
<CreateProjectVersionModal
v-if="currentMember"
ref="create-project-version-modal"
></CreateProjectVersionModal>
<ConfirmModal
v-if="currentMember"
ref="deleteVersionModal"
title="Are you sure you want to delete this version?"
description="This will remove this version forever (like really forever)."
:has-to-type="false"
proceed-label="Delete"
@proceed="deleteVersion()"
/>
<Admonition v-if="!hideVersionsAdmonition && currentMember" type="info" class="mb-4"> <Admonition v-if="!hideVersionsAdmonition && currentMember" type="info" class="mb-4">
Managing project versions has moved! You can now add and edit versions in the Creating and editing project versions can now be done directly from the
<NuxtLink to="settings/versions" class="font-medium text-blue hover:underline" <NuxtLink to="settings/versions" class="font-medium text-blue hover:underline"
>project settings</NuxtLink >project settings</NuxtLink
>. >.
@@ -43,24 +58,72 @@
(version) => (version) =>
`/${project.project_type}/${ `/${project.project_type}/${
project.slug ? project.slug : project.id project.slug ? project.slug : project.id
}/version/${encodeURI(version.displayUrlEnding)}` }/version/${encodeURI(version.displayUrlEnding ? version.displayUrlEnding : version.id)}`
" "
:open-modal="currentMember ? () => handleOpenCreateVersionModal() : undefined"
> >
<template #actions="{ version }"> <template #actions="{ version }">
<ButtonStyled circular type="transparent"> <ButtonStyled circular type="transparent">
<a <a
v-tooltip="`Download`" v-tooltip="`Download`"
:href="getPrimaryFile(version).url" :href="getPrimaryFile(version).url"
class="group-hover:!bg-brand group-hover:[&>svg]:!text-brand-inverted" class="hover:!bg-button-bg [&>svg]:!text-green"
aria-label="Download" aria-label="Download"
@click="emit('onDownload')" @click="emit('onDownload')"
> >
<DownloadIcon aria-hidden="true" /> <DownloadIcon aria-hidden="true" />
</a> </a>
</ButtonStyled> </ButtonStyled>
<ButtonStyled v-if="currentMember" circular type="transparent">
<OverflowMenu
v-tooltip="'Edit version'"
class="hover:!bg-button-bg"
:dropdown-id="`${baseDropdownId}-edit-${version.id}`"
:options="[
{
id: 'edit-details',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-details'),
},
{
id: 'edit-changelog',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-changelog'),
},
{
id: 'edit-dependencies',
action: () =>
handleOpenEditVersionModal(version.id, project.id, 'add-dependencies'),
shown: project.project_type !== 'modpack',
},
{
id: 'edit-files',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-files'),
},
]"
aria-label="Edit version"
>
<EditIcon aria-hidden="true" />
<template #edit-files>
<FileIcon aria-hidden="true" />
Edit files
</template>
<template #edit-details>
<InfoIcon aria-hidden="true" />
Edit details
</template>
<template #edit-dependencies>
<BoxIcon aria-hidden="true" />
Edit dependencies
</template>
<template #edit-changelog>
<AlignLeftIcon aria-hidden="true" />
Edit changelog
</template>
</OverflowMenu>
</ButtonStyled>
<ButtonStyled circular type="transparent"> <ButtonStyled circular type="transparent">
<OverflowMenu <OverflowMenu
class="group-hover:!bg-button-bg" v-tooltip="'More options'"
class="hover:!bg-button-bg"
:dropdown-id="`${baseDropdownId}-${version.id}`" :dropdown-id="`${baseDropdownId}-${version.id}`"
:options="[ :options="[
{ {
@@ -116,6 +179,38 @@
}, },
shown: flags.developerMode, shown: flags.developerMode,
}, },
{ divider: true, shown: !!currentMember },
{
id: 'edit-details',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-details'),
shown: !!currentMember,
},
{
id: 'edit-changelog',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-changelog'),
shown: !!currentMember,
},
{
id: 'edit-dependencies',
action: () =>
handleOpenEditVersionModal(version.id, project.id, 'add-dependencies'),
shown: !!currentMember && project.project_type !== 'modpack',
},
{
id: 'edit-files',
action: () => handleOpenEditVersionModal(version.id, project.id, 'add-files'),
shown: !!currentMember,
},
{
id: 'delete',
color: 'red',
hoverFilled: true,
action: () => {
selectedVersion = version.id
deleteVersionModal?.show()
},
shown: !!currentMember,
},
]" ]"
aria-label="More options" aria-label="More options"
> >
@@ -140,6 +235,26 @@
<ReportIcon aria-hidden="true" /> <ReportIcon aria-hidden="true" />
Report Report
</template> </template>
<template #edit-files>
<FileIcon aria-hidden="true" />
Edit files
</template>
<template #edit-details>
<InfoIcon aria-hidden="true" />
Edit details
</template>
<template #edit-dependencies>
<BoxIcon aria-hidden="true" />
Edit dependencies
</template>
<template #edit-changelog>
<AlignLeftIcon aria-hidden="true" />
Edit changelog
</template>
<template #delete>
<TrashIcon aria-hidden="true" />
Delete
</template>
<template #copy-id> <template #copy-id>
<ClipboardCopyIcon aria-hidden="true" /> <ClipboardCopyIcon aria-hidden="true" />
Copy ID Copy ID
@@ -166,18 +281,35 @@
<script setup> <script setup>
import { import {
AlignLeftIcon,
BoxIcon,
ClipboardCopyIcon, ClipboardCopyIcon,
DownloadIcon, DownloadIcon,
EditIcon,
ExternalIcon, ExternalIcon,
FileIcon,
InfoIcon,
LinkIcon, LinkIcon,
MoreVerticalIcon, MoreVerticalIcon,
ReportIcon, ReportIcon,
SettingsIcon, SettingsIcon,
ShareIcon, ShareIcon,
TrashIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { Admonition, ButtonStyled, OverflowMenu, ProjectPageVersions } from '@modrinth/ui' import {
Admonition,
ButtonStyled,
ConfirmModal,
injectModrinthClient,
injectNotificationManager,
injectProjectPageContext,
OverflowMenu,
ProjectPageVersions,
} from '@modrinth/ui'
import { useLocalStorage } from '@vueuse/core' import { useLocalStorage } from '@vueuse/core'
import { useTemplateRef } from 'vue'
import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue'
import { reportVersion } from '~/utils/report-helpers.ts' import { reportVersion } from '~/utils/report-helpers.ts'
const props = defineProps({ const props = defineProps({
@@ -205,6 +337,24 @@ const tags = useGeneratedState()
const flags = useFeatureFlags() const flags = useFeatureFlags()
const auth = await useAuth() const auth = await useAuth()
const client = injectModrinthClient()
const { addNotification } = injectNotificationManager()
const { refreshVersions } = injectProjectPageContext()
const deleteVersionModal = ref()
const selectedVersion = ref(null)
const createProjectVersionModal = useTemplateRef('create-project-version-modal')
const handleOpenCreateVersionModal = () => {
if (!props.currentMember) return
createProjectVersionModal.value?.openCreateVersionModal()
}
const handleOpenEditVersionModal = (versionId, projectId, stageId) => {
if (!props.currentMember) return
createProjectVersionModal.value?.openEditVersionModal(versionId, projectId, stageId)
}
const hideVersionsAdmonition = useLocalStorage( const hideVersionsAdmonition = useLocalStorage(
'hideVersionsHasMovedAdmonition', 'hideVersionsHasMovedAdmonition',
!props.versions.length, !props.versions.length,
@@ -223,4 +373,32 @@ function getPrimaryFile(version) {
async function copyToClipboard(text) { async function copyToClipboard(text) {
await navigator.clipboard.writeText(text) await navigator.clipboard.writeText(text)
} }
async function deleteVersion() {
const id = selectedVersion.value
if (!id) return
startLoading()
try {
await client.labrinth.versions_v3.deleteVersion(id)
addNotification({
title: 'Version deleted',
text: 'The version has been successfully deleted.',
type: 'success',
})
} catch (err) {
addNotification({
title: 'An error occurred',
text: err.data ? err.data.description : err,
type: 'error',
})
}
refreshVersions()
selectedVersion.value = null
stopLoading()
}
</script> </script>

View File

@@ -1,4 +1,5 @@
import type { Labrinth } from '@modrinth/api-client' import type { Labrinth } from '@modrinth/api-client'
import { SaveIcon, SpinnerIcon } from '@modrinth/assets'
import { import {
createContext, createContext,
injectModrinthClient, injectModrinthClient,
@@ -6,6 +7,7 @@ import {
injectProjectPageContext, injectProjectPageContext,
type MultiStageModal, type MultiStageModal,
resolveCtxFn, resolveCtxFn,
type StageButtonConfig,
type StageConfigInput, type StageConfigInput,
} from '@modrinth/ui' } from '@modrinth/ui'
import JSZip from 'jszip' import JSZip from 'jszip'
@@ -49,9 +51,9 @@ export type VersionStage =
| 'add-environment' | 'add-environment'
| 'add-dependencies' | 'add-dependencies'
| 'add-changelog' | 'add-changelog'
| 'edit-loaders' | 'from-details-loaders'
| 'edit-mc-versions' | 'from-details-mc-versions'
| 'edit-environment' | 'from-details-environment'
export interface ManageVersionContextValue { export interface ManageVersionContextValue {
// State // State
@@ -75,6 +77,7 @@ export interface ManageVersionContextValue {
// Stage helpers // Stage helpers
getNextLabel: (currentIndex?: number | null) => string getNextLabel: (currentIndex?: number | null) => string
saveButtonConfig: () => StageButtonConfig
// Version methods // Version methods
newDraftVersion: (projectId: string, version?: Labrinth.Versions.v3.DraftVersion | null) => void newDraftVersion: (projectId: string, version?: Labrinth.Versions.v3.DraftVersion | null) => void
@@ -264,7 +267,7 @@ export function createManageVersionContext(
if (noEnvironmentProject.value) version.environment = undefined if (noEnvironmentProject.value) version.environment = undefined
try { try {
await labrinth.versions_v3.createVersion(version, files) await labrinth.versions_v3.createVersion(version, files, projectType.value ?? null)
modal.value?.hide() modal.value?.hide()
addNotification({ addNotification({
title: 'Project version created', title: 'Project version created',
@@ -380,6 +383,16 @@ export function createManageVersionContext(
} }
} }
const saveButtonConfig = (): StageButtonConfig => ({
label: 'Save changes',
icon: isSubmitting.value ? SpinnerIcon : SaveIcon,
iconPosition: 'before',
iconClass: isSubmitting.value ? 'animate-spin' : undefined,
color: 'green',
disabled: isSubmitting.value,
onClick: () => handleSaveVersionEdits(),
})
const contextValue: ManageVersionContextValue = { const contextValue: ManageVersionContextValue = {
// State // State
draftVersion, draftVersion,
@@ -402,6 +415,7 @@ export function createManageVersionContext(
// Stage helpers // Stage helpers
getNextLabel, getNextLabel,
saveButtonConfig,
// Methods // Methods
newDraftVersion, newDraftVersion,

View File

@@ -1,4 +1,4 @@
import { LeftArrowIcon, PlusIcon, SpinnerIcon } from '@modrinth/assets' import { LeftArrowIcon, PlusIcon, SaveIcon, SpinnerIcon, XIcon } from '@modrinth/assets'
import type { StageConfigInput } from '@modrinth/ui' import type { StageConfigInput } from '@modrinth/ui'
import { markRaw } from 'vue' import { markRaw } from 'vue'
@@ -10,14 +10,21 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'add-changelog', id: 'add-changelog',
stageContent: markRaw(AddChangelogStage), stageContent: markRaw(AddChangelogStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit changelog' : 'Add changelog'), title: (ctx) => (ctx.editingVersion.value ? 'Edit changelog' : 'Add changelog'),
leftButtonConfig: (ctx) => ({ leftButtonConfig: (ctx) =>
label: 'Back', ctx.editingVersion.value
icon: LeftArrowIcon, ? {
onClick: () => ctx.modal.value?.prevStage(), label: 'Cancel',
}), icon: XIcon,
onClick: () => ctx.modal.value?.hide(),
}
: {
label: 'Back',
icon: LeftArrowIcon,
onClick: () => ctx.modal.value?.prevStage(),
},
rightButtonConfig: (ctx) => ({ rightButtonConfig: (ctx) => ({
label: ctx.editingVersion.value ? 'Save changes' : 'Create version', label: ctx.editingVersion.value ? 'Save changes' : 'Create version',
icon: ctx.isSubmitting.value ? SpinnerIcon : PlusIcon, icon: ctx.isSubmitting.value ? SpinnerIcon : ctx.editingVersion.value ? SaveIcon : PlusIcon,
iconPosition: 'before', iconPosition: 'before',
iconClass: ctx.isSubmitting.value ? 'animate-spin' : undefined, iconClass: ctx.isSubmitting.value ? 'animate-spin' : undefined,
color: 'green', color: 'green',
@@ -25,4 +32,5 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
onClick: () => onClick: () =>
ctx.editingVersion.value ? ctx.handleSaveVersionEdits() : ctx.handleCreateVersion(), ctx.editingVersion.value ? ctx.handleSaveVersionEdits() : ctx.handleCreateVersion(),
}), }),
nonProgressStage: (ctx) => ctx.editingVersion.value,
} }

View File

@@ -1,4 +1,4 @@
import { LeftArrowIcon, RightArrowIcon } from '@modrinth/assets' import { LeftArrowIcon, RightArrowIcon, XIcon } from '@modrinth/assets'
import type { StageConfigInput } from '@modrinth/ui' import type { StageConfigInput } from '@modrinth/ui'
import { markRaw } from 'vue' import { markRaw } from 'vue'
@@ -11,15 +11,26 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
stageContent: markRaw(AddDependenciesStage), stageContent: markRaw(AddDependenciesStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit dependencies' : 'Add dependencies'), title: (ctx) => (ctx.editingVersion.value ? 'Edit dependencies' : 'Add dependencies'),
skip: (ctx) => ctx.projectType.value === 'modpack', skip: (ctx) => ctx.projectType.value === 'modpack',
leftButtonConfig: (ctx) => ({ leftButtonConfig: (ctx) =>
label: 'Back', ctx.editingVersion.value
icon: LeftArrowIcon, ? {
onClick: () => ctx.modal.value?.prevStage(), label: 'Cancel',
}), icon: XIcon,
rightButtonConfig: (ctx) => ({ onClick: () => ctx.modal.value?.hide(),
label: ctx.getNextLabel(), }
icon: RightArrowIcon, : {
iconPosition: 'after', label: 'Back',
onClick: () => ctx.modal.value?.nextStage(), icon: LeftArrowIcon,
}), onClick: () => ctx.modal.value?.prevStage(),
},
rightButtonConfig: (ctx) =>
ctx.editingVersion.value
? ctx.saveButtonConfig()
: {
label: ctx.getNextLabel(),
icon: RightArrowIcon,
iconPosition: 'after',
onClick: () => ctx.modal.value?.nextStage(),
},
nonProgressStage: (ctx) => ctx.editingVersion.value,
} }

View File

@@ -1,4 +1,4 @@
import { LeftArrowIcon, RightArrowIcon } from '@modrinth/assets' import { LeftArrowIcon, RightArrowIcon, XIcon } from '@modrinth/assets'
import type { StageConfigInput } from '@modrinth/ui' import type { StageConfigInput } from '@modrinth/ui'
import { markRaw } from 'vue' import { markRaw } from 'vue'
@@ -10,16 +10,31 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'add-details', id: 'add-details',
stageContent: markRaw(AddDetailsStage), stageContent: markRaw(AddDetailsStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit details' : 'Add details'), title: (ctx) => (ctx.editingVersion.value ? 'Edit details' : 'Add details'),
leftButtonConfig: (ctx) => ({ leftButtonConfig: (ctx) =>
label: 'Back', ctx.editingVersion.value
icon: LeftArrowIcon, ? {
onClick: () => ctx.modal.value?.prevStage(), label: 'Cancel',
}), icon: XIcon,
rightButtonConfig: (ctx) => ({ onClick: () => ctx.modal.value?.hide(),
label: ctx.getNextLabel(), }
icon: RightArrowIcon, : {
iconPosition: 'after', label: 'Back',
disabled: ctx.draftVersion.value.version_number.trim().length === 0, icon: LeftArrowIcon,
onClick: () => ctx.modal.value?.nextStage(), onClick: () => ctx.modal.value?.prevStage(),
}), },
rightButtonConfig: (ctx) =>
ctx.editingVersion.value
? {
...ctx.saveButtonConfig(),
disabled:
ctx.draftVersion.value.version_number.trim().length === 0 || ctx.isSubmitting.value,
}
: {
label: ctx.getNextLabel(),
icon: RightArrowIcon,
iconPosition: 'after',
disabled: ctx.draftVersion.value.version_number.trim().length === 0,
onClick: () => ctx.modal.value?.nextStage(),
},
nonProgressStage: (ctx) => ctx.editingVersion.value,
} }

View File

@@ -28,8 +28,8 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
}), }),
} }
export const editStageConfig: StageConfigInput<ManageVersionContextValue> = { export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'edit-environment', id: 'from-details-environment',
stageContent: markRaw(AddEnvironmentStage), stageContent: markRaw(AddEnvironmentStage),
title: 'Edit environment', title: 'Edit environment',
nonProgressStage: true, nonProgressStage: true,
@@ -39,11 +39,17 @@ export const editStageConfig: StageConfigInput<ManageVersionContextValue> = {
disabled: !ctx.draftVersion.value.environment, disabled: !ctx.draftVersion.value.environment,
onClick: () => ctx.modal.value?.setStage('add-details'), onClick: () => ctx.modal.value?.setStage('add-details'),
}), }),
rightButtonConfig: (ctx) => ({ rightButtonConfig: (ctx) =>
label: ctx.getNextLabel(2), ctx.editingVersion.value
icon: RightArrowIcon, ? {
iconPosition: 'after', ...ctx.saveButtonConfig(),
disabled: !ctx.draftVersion.value.environment, disabled: !ctx.draftVersion.value.environment,
onClick: () => ctx.modal.value?.setStage(2), }
}), : {
label: ctx.getNextLabel(2),
icon: RightArrowIcon,
iconPosition: 'after',
disabled: !ctx.draftVersion.value.environment,
onClick: () => ctx.modal.value?.setStage(2),
},
} }

View File

@@ -15,6 +15,13 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
ctx.filesToAdd.value.length !== 0 || ctx.filesToAdd.value.length !== 0 ||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0 (ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
if (ctx.editingVersion.value)
return {
label: 'Cancel',
icon: XIcon,
onClick: () => ctx.modal.value?.hide(),
}
if (!hasFiles) return null if (!hasFiles) return null
return { return {
@@ -28,6 +35,13 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
ctx.filesToAdd.value.length !== 0 || ctx.filesToAdd.value.length !== 0 ||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0 (ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
if (ctx.editingVersion.value)
return {
...ctx.saveButtonConfig(),
label: 'Save files',
disabled: ctx.isSubmitting.value,
}
if (!hasFiles) return null if (!hasFiles) return null
return { return {
@@ -38,4 +52,5 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
onClick: () => ctx.modal.value?.nextStage(), onClick: () => ctx.modal.value?.nextStage(),
} }
}, },
nonProgressStage: (ctx) => ctx.editingVersion.value,
} }

View File

@@ -28,8 +28,8 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
}), }),
} }
export const editStageConfig: StageConfigInput<ManageVersionContextValue> = { export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'edit-loaders', id: 'from-details-loaders',
stageContent: markRaw(AddLoadersStage), stageContent: markRaw(AddLoadersStage),
title: 'Edit loaders', title: 'Edit loaders',
nonProgressStage: true, nonProgressStage: true,
@@ -39,11 +39,17 @@ export const editStageConfig: StageConfigInput<ManageVersionContextValue> = {
disabled: ctx.draftVersion.value.loaders.length === 0, disabled: ctx.draftVersion.value.loaders.length === 0,
onClick: () => ctx.modal.value?.setStage('add-details'), onClick: () => ctx.modal.value?.setStage('add-details'),
}), }),
rightButtonConfig: (ctx) => ({ rightButtonConfig: (ctx) =>
label: ctx.getNextLabel(2), ctx.editingVersion.value
icon: RightArrowIcon, ? {
iconPosition: 'after', ...ctx.saveButtonConfig(),
disabled: ctx.draftVersion.value.loaders.length === 0, disabled: ctx.draftVersion.value.loaders.length === 0,
onClick: () => ctx.modal.value?.setStage(2), }
}), : {
label: ctx.getNextLabel(2),
icon: RightArrowIcon,
iconPosition: 'after',
disabled: ctx.draftVersion.value.loaders.length === 0,
onClick: () => ctx.modal.value?.setStage(2),
},
} }

View File

@@ -26,8 +26,8 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
}), }),
} }
export const editStageConfig: StageConfigInput<ManageVersionContextValue> = { export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'edit-mc-versions', id: 'from-details-mc-versions',
stageContent: markRaw(AddMcVersionsStage), stageContent: markRaw(AddMcVersionsStage),
title: 'Edit game versions', title: 'Edit game versions',
nonProgressStage: true, nonProgressStage: true,
@@ -37,11 +37,17 @@ export const editStageConfig: StageConfigInput<ManageVersionContextValue> = {
disabled: ctx.draftVersion.value.game_versions.length === 0, disabled: ctx.draftVersion.value.game_versions.length === 0,
onClick: () => ctx.modal.value?.setStage('add-details'), onClick: () => ctx.modal.value?.setStage('add-details'),
}), }),
rightButtonConfig: (ctx) => ({ rightButtonConfig: (ctx) =>
label: ctx.getNextLabel(2), ctx.editingVersion.value
icon: RightArrowIcon, ? {
iconPosition: 'after', ...ctx.saveButtonConfig(),
disabled: ctx.draftVersion.value.game_versions.length === 0, disabled: ctx.draftVersion.value.game_versions.length === 0,
onClick: () => ctx.modal.value?.setStage(2), }
}), : {
label: ctx.getNextLabel(2),
icon: RightArrowIcon,
iconPosition: 'after',
disabled: ctx.draftVersion.value.game_versions.length === 0,
onClick: () => ctx.modal.value?.setStage(2),
},
} }

View File

@@ -2,16 +2,16 @@ import { stageConfig as addChangelogStageConfig } from './add-changelog'
import { stageConfig as addDependenciesStageConfig } from './add-dependencies' import { stageConfig as addDependenciesStageConfig } from './add-dependencies'
import { stageConfig as addDetailsStageConfig } from './add-details' import { stageConfig as addDetailsStageConfig } from './add-details'
import { import {
editStageConfig as editEnvironmentStageConfig, fromDetailsStageConfig as editEnvironmentStageConfig,
stageConfig as addEnvironmentStageConfig, stageConfig as addEnvironmentStageConfig,
} from './add-environment' } from './add-environment'
import { stageConfig as addFilesStageConfig } from './add-files' import { stageConfig as addFilesStageConfig } from './add-files'
import { import {
editStageConfig as editLoadersStageConfig, fromDetailsStageConfig as editLoadersStageConfig,
stageConfig as addLoadersStageConfig, stageConfig as addLoadersStageConfig,
} from './add-loaders' } from './add-loaders'
import { import {
editStageConfig as editMcVersionsStageConfig, fromDetailsStageConfig as editMcVersionsStageConfig,
stageConfig as addMcVersionsStageConfig, stageConfig as addMcVersionsStageConfig,
} from './add-mc-versions' } from './add-mc-versions'

View File

@@ -477,6 +477,7 @@ export namespace Labrinth {
downloads: number downloads: number
files: VersionFile[] files: VersionFile[]
environment?: Labrinth.Projects.v3.Environment environment?: Labrinth.Projects.v3.Environment
mrpack_loaders?: string[]
} }
export interface DraftVersionFile { export interface DraftVersionFile {
@@ -514,6 +515,7 @@ export namespace Labrinth {
primary_file?: string primary_file?: string
file_types?: Record<string, Labrinth.Versions.v3.FileType | null> file_types?: Record<string, Labrinth.Versions.v3.FileType | null>
environment?: Labrinth.Projects.v3.Environment environment?: Labrinth.Projects.v3.Environment
mrpack_loaders?: string[]
} }
export type ModifyVersionRequest = Partial< export type ModifyVersionRequest = Partial<

View File

@@ -139,6 +139,7 @@ export class LabrinthVersionsV3Module extends AbstractModule {
public async createVersion( public async createVersion(
draftVersion: Labrinth.Versions.v3.DraftVersion, draftVersion: Labrinth.Versions.v3.DraftVersion,
versionFiles: Labrinth.Versions.v3.DraftVersionFile[], versionFiles: Labrinth.Versions.v3.DraftVersionFile[],
projectType: Labrinth.Projects.v2.ProjectType | null = null,
): Promise<Labrinth.Versions.v3.Version> { ): Promise<Labrinth.Versions.v3.Version> {
const formData = new FormData() const formData = new FormData()
@@ -164,13 +165,18 @@ export class LabrinthVersionsV3Module extends AbstractModule {
changelog: draftVersion.changelog, changelog: draftVersion.changelog,
dependencies: draftVersion.dependencies || [], dependencies: draftVersion.dependencies || [],
game_versions: draftVersion.game_versions, game_versions: draftVersion.game_versions,
loaders: draftVersion.loaders,
version_type: draftVersion.version_type, version_type: draftVersion.version_type,
featured: !!draftVersion.featured, featured: !!draftVersion.featured,
file_parts: fileParts, file_parts: fileParts,
file_types: fileTypeMap, file_types: fileTypeMap,
primary_file: fileParts[0], primary_file: fileParts[0],
environment: draftVersion.environment, environment: draftVersion.environment,
loaders: draftVersion.loaders,
}
if (projectType === 'modpack') {
data.mrpack_loaders = draftVersion.loaders
data.loaders = ['mrpack']
} }
formData.append('data', JSON.stringify(data)) formData.append('data', JSON.stringify(data))
@@ -181,7 +187,7 @@ export class LabrinthVersionsV3Module extends AbstractModule {
const newVersion = await this.client.request<Labrinth.Versions.v3.Version>(`/version`, { const newVersion = await this.client.request<Labrinth.Versions.v3.Version>(`/version`, {
api: 'labrinth', api: 'labrinth',
version: 2, version: 3,
method: 'POST', method: 'POST',
body: formData, body: formData,
timeout: 120000, timeout: 120000,
@@ -190,10 +196,6 @@ export class LabrinthVersionsV3Module extends AbstractModule {
}, },
}) })
await this.modifyVersion(newVersion.id, {
environment: draftVersion.environment,
})
return newVersion return newVersion
} }

View File

@@ -14,15 +14,13 @@
</template> </template>
<progress <progress
v-if="currentStage?.nonProgressStage !== true" v-if="nonProgressStage !== true"
:value="progressValue" :value="progressValue"
max="100" max="100"
class="w-full h-1 appearance-none border-none absolute top-0 left-0" class="w-full h-1 appearance-none border-none absolute top-0 left-0"
></progress> ></progress>
<div class="sm:w-[512px]"> <component :is="currentStage?.stageContent" />
<component :is="currentStage?.stageContent" />
</div>
<template #actions> <template #actions>
<div <div
@@ -81,7 +79,7 @@ export interface StageConfigInput<T> {
stageContent: Component stageContent: Component
title: MaybeCtxFn<T, string> title: MaybeCtxFn<T, string>
skip?: MaybeCtxFn<T, boolean> skip?: MaybeCtxFn<T, boolean>
nonProgressStage?: boolean nonProgressStage?: MaybeCtxFn<T, boolean>
leftButtonConfig: MaybeCtxFn<T, StageButtonConfig | null> leftButtonConfig: MaybeCtxFn<T, StageButtonConfig | null>
rightButtonConfig: MaybeCtxFn<T, StageButtonConfig | null> rightButtonConfig: MaybeCtxFn<T, StageButtonConfig | null>
} }
@@ -174,9 +172,15 @@ const rightButtonConfig = computed(() => {
return resolveCtxFn(stage.rightButtonConfig, props.context) return resolveCtxFn(stage.rightButtonConfig, props.context)
}) })
const nonProgressStage = computed(() => {
const stage = currentStage.value
if (!stage) return false
return resolveCtxFn(stage.nonProgressStage, props.context)
})
const progressValue = computed(() => { const progressValue = computed(() => {
const isProgressStage = (stage: StageConfigInput<T>) => { const isProgressStage = (stage: StageConfigInput<T>) => {
if (stage.nonProgressStage) return false if (resolveCtxFn(stage.nonProgressStage, props.context)) return false
const skip = stage.skip ? resolveCtxFn(stage.skip, props.context) : false const skip = stage.skip ? resolveCtxFn(stage.skip, props.context) : false
return !skip return !skip
} }

View File

@@ -9,7 +9,7 @@
@update:query="updateQuery" @update:query="updateQuery"
/> />
<ButtonStyled v-if="openModal" color="green"> <ButtonStyled v-if="openModal" :color="createVersionButtonSecondary ? 'standard' : 'green'">
<button @click="openModal"><PlusIcon /> Create version</button> <button @click="openModal"><PlusIcon /> Create version</button>
</ButtonStyled> </ButtonStyled>
@@ -238,6 +238,7 @@ const props = withDefaults(
gameVersions: GameVersionTag[] gameVersions: GameVersionTag[]
versionLink?: (version: Version) => string versionLink?: (version: Version) => string
openModal?: () => void openModal?: () => void
createVersionButtonSecondary?: boolean
}>(), }>(),
{ {
baseId: undefined, baseId: undefined,