feat: add edit version pages tabs (#5841)

* feat: add edit version pages tabs

* feat: switch to nav tabs instead of chips

* feat: show "Edit version" as modal title instead of specific page
This commit is contained in:
Truman Gao
2026-04-18 11:13:03 -06:00
committed by GitHub
parent b9e7b54b4e
commit ed2f04322f
12 changed files with 87 additions and 17 deletions

View File

@@ -1,4 +1,12 @@
<template> <template>
<NavTabs
v-if="editingVersion"
mode="local"
:links="editTabLinks"
:active-index="2"
class="mb-4 border border-solid border-surface-5 shadow-none drop-shadow-none"
@tab-click="setEditTab"
/>
<div class="flex w-full flex-col gap-4"> <div class="flex w-full flex-col gap-4">
<template <template
v-if="handlingNewFiles || !(filesToAdd.length || draftVersion.existing_files?.length)" v-if="handlingNewFiles || !(filesToAdd.length || draftVersion.existing_files?.length)"
@@ -91,6 +99,7 @@ import {
defineMessages, defineMessages,
DropzoneFileInput, DropzoneFileInput,
injectProjectPageContext, injectProjectPageContext,
NavTabs,
useVIntl, useVIntl,
} from '@modrinth/ui' } from '@modrinth/ui'
import { acceptFileFromProjectType } from '@modrinth/utils' import { acceptFileFromProjectType } from '@modrinth/utils'
@@ -110,10 +119,25 @@ const {
swapPrimaryFile, swapPrimaryFile,
replacePrimaryFile, replacePrimaryFile,
editingVersion, editingVersion,
modal,
primaryFile, primaryFile,
handleNewFiles, handleNewFiles,
} = injectManageVersionContext() } = injectManageVersionContext()
const editTabs = [
{ label: 'Metadata', href: 'metadata', stage: 'metadata' },
{ label: 'Details', href: 'details', stage: 'add-details' },
{ label: 'Files', href: 'files', stage: 'add-files' },
] as const
const editTabLinks = editTabs.map(({ label, href }) => ({ label, href }))
function setEditTab(index: number) {
const tab = editTabs[index]
if (!tab) return
modal.value?.setStage(tab.stage)
}
function handleRemoveFile(index: number) { function handleRemoveFile(index: number) {
filesToAdd.value.splice(index, 1) filesToAdd.value.splice(index, 1)
} }

View File

@@ -1,4 +1,12 @@
<template> <template>
<NavTabs
v-if="editingVersion"
mode="local"
:links="editTabLinks"
:active-index="1"
class="mb-4 border border-solid border-surface-5 shadow-none drop-shadow-none"
@tab-click="setEditTab"
/>
<div class="flex w-full flex-col gap-6"> <div class="flex w-full flex-col gap-6">
<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">
@@ -53,12 +61,26 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Chips, MarkdownEditor, StyledInput } from '@modrinth/ui' import { Chips, MarkdownEditor, NavTabs, StyledInput } from '@modrinth/ui'
import { useImageUpload } from '~/composables/image-upload.ts' import { useImageUpload } from '~/composables/image-upload.ts'
import { injectManageVersionContext } from '~/providers/version/manage-version-modal' import { injectManageVersionContext } from '~/providers/version/manage-version-modal'
const { draftVersion, isUploading } = injectManageVersionContext() const { draftVersion, isUploading, editingVersion, modal } = injectManageVersionContext()
const editTabs = [
{ label: 'Metadata', href: 'metadata', stage: 'metadata' },
{ label: 'Details', href: 'details', stage: 'add-details' },
{ label: 'Files', href: 'files', stage: 'add-files' },
] as const
const editTabLinks = editTabs.map(({ label, href }) => ({ label, href }))
function setEditTab(index: number) {
const tab = editTabs[index]
if (!tab) return
modal.value?.setStage(tab.stage)
}
async function onImageUpload(file: File) { async function onImageUpload(file: File) {
const response = await useImageUpload(file, { context: 'version' }) const response = await useImageUpload(file, { context: 'version' })

View File

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

View File

@@ -1,4 +1,12 @@
<template> <template>
<NavTabs
v-if="editingVersion"
mode="local"
:links="editTabLinks"
:active-index="0"
class="mb-2 border border-solid border-surface-5 shadow-none drop-shadow-none"
@tab-click="setEditTab"
/>
<div class="flex flex-col gap-6"> <div class="flex flex-col gap-6">
<div v-if="!editingVersion" class="flex flex-col gap-1"> <div v-if="!editingVersion" class="flex flex-col gap-1">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
@@ -202,6 +210,7 @@ import {
ENVIRONMENTS_COPY, ENVIRONMENTS_COPY,
FormattedTag, FormattedTag,
injectProjectPageContext, injectProjectPageContext,
NavTabs,
TagItem, TagItem,
useVIntl, useVIntl,
} from '@modrinth/ui' } from '@modrinth/ui'
@@ -229,6 +238,21 @@ const { projectV2 } = injectProjectPageContext()
const generatedState = useGeneratedState() const generatedState = useGeneratedState()
const loaders = computed(() => generatedState.value.loaders) const loaders = computed(() => generatedState.value.loaders)
const editTabs = [
{ label: 'Metadata', href: 'metadata', stage: 'metadata' },
{ label: 'Details', href: 'details', stage: 'add-details' },
{ label: 'Files', href: 'files', stage: 'add-files' },
] as const
const editTabLinks = editTabs.map(({ label, href }) => ({ label, href }))
function setEditTab(index: number) {
const tab = editTabs[index]
if (!tab) return
modal.value?.setStage(tab.stage)
}
const isModpack = computed(() => projectType.value === 'modpack') const isModpack = computed(() => projectType.value === 'modpack')
const isResourcePack = computed( const isResourcePack = computed(
() => () =>

View File

@@ -9,7 +9,7 @@ import type { ManageVersionContextValue } from '../manage-version-modal'
export const stageConfig: StageConfigInput<ManageVersionContextValue> = { export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'add-files', id: 'add-files',
stageContent: markRaw(AddFilesStage), stageContent: markRaw(AddFilesStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit files' : 'Files'), title: (ctx) => (ctx.editingVersion.value ? 'Edit version' : 'Files'),
nonProgressStage: (ctx) => ctx.editingVersion.value, nonProgressStage: (ctx) => ctx.editingVersion.value,
cannotNavigateForward: (ctx) => { cannotNavigateForward: (ctx) => {
const hasFiles = const hasFiles =
@@ -64,7 +64,7 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = { export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'from-details-files', id: 'from-details-files',
stageContent: markRaw(AddFilesStage), stageContent: markRaw(AddFilesStage),
title: 'Edit files', title: 'Edit version',
nonProgressStage: true, nonProgressStage: true,
leftButtonConfig: (ctx) => { leftButtonConfig: (ctx) => {
const hasFiles = const hasFiles =

View File

@@ -9,7 +9,7 @@ import type { ManageVersionContextValue } from '../manage-version-modal'
export const stageConfig: StageConfigInput<ManageVersionContextValue> = { export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'add-dependencies', id: 'add-dependencies',
stageContent: markRaw(DependenciesStage), stageContent: markRaw(DependenciesStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit dependencies' : 'Dependencies'), title: (ctx) => (ctx.editingVersion.value ? 'Edit version' : 'Dependencies'),
skip: (ctx) => ctx.suggestedDependencies.value != null || ctx.projectType.value === 'modpack', skip: (ctx) => ctx.suggestedDependencies.value != null || ctx.projectType.value === 'modpack',
leftButtonConfig: (ctx) => leftButtonConfig: (ctx) =>
ctx.editingVersion.value ctx.editingVersion.value
@@ -38,7 +38,7 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = { export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'from-details-dependencies', id: 'from-details-dependencies',
stageContent: markRaw(DependenciesStage), stageContent: markRaw(DependenciesStage),
title: 'Edit dependencies', title: 'Edit version',
nonProgressStage: true, nonProgressStage: true,
leftButtonConfig: (ctx) => ({ leftButtonConfig: (ctx) => ({
label: 'Back', label: 'Back',

View File

@@ -9,7 +9,7 @@ import type { ManageVersionContextValue } from '../manage-version-modal'
export const stageConfig: StageConfigInput<ManageVersionContextValue> = { export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'add-details', id: 'add-details',
stageContent: markRaw(DetailsStage), stageContent: markRaw(DetailsStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit details' : 'Details'), title: (ctx) => (ctx.editingVersion.value ? 'Edit version' : 'Details'),
maxWidth: '744px', maxWidth: '744px',
disableClose: (ctx) => ctx.isUploading.value, disableClose: (ctx) => ctx.isUploading.value,
leftButtonConfig: (ctx) => leftButtonConfig: (ctx) =>

View File

@@ -9,7 +9,7 @@ import type { ManageVersionContextValue } from '../manage-version-modal'
export const stageConfig: StageConfigInput<ManageVersionContextValue> = { export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'add-environment', id: 'add-environment',
stageContent: markRaw(EnvironmentStage), stageContent: markRaw(EnvironmentStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit environment' : 'Environment'), title: (ctx) => (ctx.editingVersion.value ? 'Edit version' : 'Environment'),
skip: (ctx) => skip: (ctx) =>
ctx.noEnvironmentProject.value || ctx.noEnvironmentProject.value ||
(!ctx.editingVersion.value && !!ctx.inferredVersionData.value?.environment) || (!ctx.editingVersion.value && !!ctx.inferredVersionData.value?.environment) ||
@@ -33,7 +33,7 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = { export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'from-details-environment', id: 'from-details-environment',
stageContent: markRaw(EnvironmentStage), stageContent: markRaw(EnvironmentStage),
title: 'Edit environment', title: 'Edit version',
nonProgressStage: true, nonProgressStage: true,
leftButtonConfig: (ctx) => ({ leftButtonConfig: (ctx) => ({
label: 'Back', label: 'Back',

View File

@@ -9,7 +9,7 @@ import type { ManageVersionContextValue } from '../manage-version-modal'
export const stageConfig: StageConfigInput<ManageVersionContextValue> = { export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'add-loaders', id: 'add-loaders',
stageContent: markRaw(LoadersStage), stageContent: markRaw(LoadersStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit loaders' : 'Loaders'), title: (ctx) => (ctx.editingVersion.value ? 'Edit version' : 'Loaders'),
skip: (ctx) => { skip: (ctx) => {
const inferredLoadersLength = ctx.inferredVersionData.value?.loaders?.length ?? 0 const inferredLoadersLength = ctx.inferredVersionData.value?.loaders?.length ?? 0
return ( return (

View File

@@ -9,7 +9,7 @@ import type { ManageVersionContextValue } from '../manage-version-modal'
export const stageConfig: StageConfigInput<ManageVersionContextValue> = { export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'add-mc-versions', id: 'add-mc-versions',
stageContent: markRaw(McVersionsStage), stageContent: markRaw(McVersionsStage),
title: (ctx) => (ctx.editingVersion.value ? 'Edit game versions' : 'Game versions'), title: (ctx) => (ctx.editingVersion.value ? 'Edit version' : 'Game versions'),
skip: (ctx) => skip: (ctx) =>
(ctx.inferredVersionData.value?.game_versions?.length ?? 0) > 0 || !ctx.primaryFile.value, (ctx.inferredVersionData.value?.game_versions?.length ?? 0) > 0 || !ctx.primaryFile.value,
hideStageInBreadcrumb: (ctx) => !ctx.primaryFile.value || ctx.handlingNewFiles.value, hideStageInBreadcrumb: (ctx) => !ctx.primaryFile.value || ctx.handlingNewFiles.value,
@@ -32,7 +32,7 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = { export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'from-details-mc-versions', id: 'from-details-mc-versions',
stageContent: markRaw(McVersionsStage), stageContent: markRaw(McVersionsStage),
title: 'Edit game versions', title: 'Edit version',
nonProgressStage: true, nonProgressStage: true,
leftButtonConfig: (ctx) => ({ leftButtonConfig: (ctx) => ({
label: 'Back', label: 'Back',

View File

@@ -9,7 +9,8 @@ import type { ManageVersionContextValue } from '../manage-version-modal'
export const stageConfig: StageConfigInput<ManageVersionContextValue> = { export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
id: 'metadata', id: 'metadata',
stageContent: markRaw(MetadataStage), stageContent: markRaw(MetadataStage),
title: 'Metadata', title: (ctx) => (ctx.editingVersion.value ? 'Edit version' : 'Metadata'),
nonProgressStage: (ctx) => ctx.editingVersion.value,
leftButtonConfig: (ctx) => leftButtonConfig: (ctx) =>
ctx.editingVersion.value ctx.editingVersion.value
? { ? {

View File

@@ -15,7 +15,7 @@
}" }"
@click="toggleItem(item)" @click="toggleItem(item)"
> >
<CheckIcon v-if="selected === item" /> <CheckIcon v-if="selected === item && !hideCheckmarkIcon" />
<span>{{ formatLabel(item) }}</span> <span>{{ formatLabel(item) }}</span>
</Button> </Button>
</div> </div>
@@ -36,6 +36,7 @@ const props = withDefaults(
ariaLabel?: string ariaLabel?: string
disabledItems?: T[] disabledItems?: T[]
disabledTooltip?: string disabledTooltip?: string
hideCheckmarkIcon?: boolean
}>(), }>(),
{ {
neverEmpty: true, neverEmpty: true,