mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 00:55:25 +00:00
update tags page + sort project types consistently (#7044)
This commit is contained in:
@@ -15,20 +15,25 @@ const emit = defineEmits<{
|
||||
(e: 'reset' | 'save', event: MouseEvent): void
|
||||
}>()
|
||||
|
||||
type SaveDisabledReason = MessageDescriptor | string
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
canReset?: boolean
|
||||
canSave?: boolean
|
||||
original: T
|
||||
modified: Partial<T>
|
||||
saving?: boolean
|
||||
text?: MessageDescriptor | string
|
||||
saveLabel?: MessageDescriptor | string
|
||||
savingLabel?: MessageDescriptor | string
|
||||
saveDisabledReason?: SaveDisabledReason | SaveDisabledReason[]
|
||||
saveIcon?: Component
|
||||
inline?: boolean
|
||||
}>(),
|
||||
{
|
||||
canReset: true,
|
||||
canSave: true,
|
||||
saving: false,
|
||||
text: () =>
|
||||
defineMessage({
|
||||
@@ -37,6 +42,7 @@ const props = withDefaults(
|
||||
}),
|
||||
saveLabel: () => commonMessages.saveButton,
|
||||
savingLabel: () => commonMessages.savingButton,
|
||||
saveDisabledReason: undefined,
|
||||
saveIcon: SaveIcon,
|
||||
inline: false,
|
||||
},
|
||||
@@ -50,6 +56,22 @@ function localizeIfPossible(message: MessageDescriptor | string) {
|
||||
return typeof message === 'string' ? message : formatMessage(message)
|
||||
}
|
||||
|
||||
const saveDisabled = computed(() => props.saving || !props.canSave)
|
||||
|
||||
const saveDisabledTooltip = computed(() => {
|
||||
if (!saveDisabled.value || props.saving || !props.saveDisabledReason) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const reasons = (
|
||||
Array.isArray(props.saveDisabledReason) ? props.saveDisabledReason : [props.saveDisabledReason]
|
||||
).map(localizeIfPossible)
|
||||
|
||||
return reasons.length > 0
|
||||
? { content: reasons.join('\n'), popperClass: 'unsaved-changes-save-tooltip' }
|
||||
: undefined
|
||||
})
|
||||
|
||||
const actionBar = ref<InstanceType<typeof FloatingActionBar> | null>(null)
|
||||
|
||||
function nudge(): void {
|
||||
@@ -66,11 +88,32 @@ defineExpose({ nudge })
|
||||
<Button v-if="canReset" type="quiet" :disabled="saving" @click="(e) => emit('reset', e)">
|
||||
<HistoryIcon /> {{ formatMessage(commonMessages.resetButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" :disabled="saving" @click="(e) => emit('save', e)">
|
||||
<SpinnerIcon v-if="saving" class="animate-spin" />
|
||||
<component :is="saveIcon" v-else />
|
||||
{{ localizeIfPossible(saving ? savingLabel : saveLabel) }}
|
||||
</Button>
|
||||
<span
|
||||
v-tooltip="saveDisabledTooltip"
|
||||
class="flex"
|
||||
:class="{ 'cursor-not-allowed': saveDisabled }"
|
||||
>
|
||||
<Button
|
||||
type="colored"
|
||||
color="brand"
|
||||
:disabled="saveDisabled"
|
||||
:class="{ 'pointer-events-none': saveDisabled }"
|
||||
@click="(e) => emit('save', e)"
|
||||
>
|
||||
<SpinnerIcon v-if="saving" class="animate-spin" />
|
||||
<component :is="saveIcon" v-else />
|
||||
{{ localizeIfPossible(saving ? savingLabel : saveLabel) }}
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
</FloatingActionBar>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
/* unscoped because floating-vue teleports the tooltip outside of scope */
|
||||
.v-popper__popper.v-popper--theme-tooltip.unsaved-changes-save-tooltip .v-popper__inner {
|
||||
max-width: 22rem;
|
||||
white-space: pre-line;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -496,7 +496,7 @@ import {
|
||||
injectPageContext,
|
||||
injectTags,
|
||||
} from '#ui/providers'
|
||||
import { commonMessages, getProjectTypeTitleMessage } from '#ui/utils'
|
||||
import { commonMessages, getProjectTypeTitleMessage, sortProjectTypes } from '#ui/utils'
|
||||
|
||||
import { blockedUsersQueryKey, injectUserProfile } from './providers'
|
||||
import {
|
||||
@@ -840,7 +840,7 @@ const projectTypes = computed(() => {
|
||||
const types = new Set(projects.value.map((project) => project.resolvedProjectType))
|
||||
if (collections.value.length > 0) types.add('collection')
|
||||
types.delete('project')
|
||||
return [...types]
|
||||
return sortProjectTypes(types)
|
||||
})
|
||||
|
||||
const navLinks = computed(() => {
|
||||
@@ -851,15 +851,13 @@ const navLinks = computed(() => {
|
||||
label: formatMessage(commonMessages.allProjectType),
|
||||
href: profilePath,
|
||||
},
|
||||
...projectTypes.value
|
||||
.map((projectType) => ({
|
||||
label:
|
||||
projectType === 'collection'
|
||||
? formatMessage(messages.collectionsLabel)
|
||||
: formatMessage(getProjectTypeTitleMessage(projectType), { count: 2 }),
|
||||
href: `${profilePath}/${projectType}s`,
|
||||
}))
|
||||
.sort((first, second) => first.label.localeCompare(second.label)),
|
||||
...projectTypes.value.map((projectType) => ({
|
||||
label:
|
||||
projectType === 'collection'
|
||||
? formatMessage(messages.collectionsLabel)
|
||||
: formatMessage(getProjectTypeTitleMessage(projectType), { count: 2 }),
|
||||
href: `${profilePath}/${projectType}s`,
|
||||
})),
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ export * from './file-extensions'
|
||||
export * from './game-modes'
|
||||
export * from './loaders'
|
||||
export * from './notices'
|
||||
export * from './project-types'
|
||||
export * from './savable'
|
||||
export * from './search'
|
||||
export * from './server-content-installing'
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
export const PROJECT_TYPE_ORDER = [
|
||||
'mod',
|
||||
'resourcepack',
|
||||
'datapack',
|
||||
'shader',
|
||||
'modpack',
|
||||
'plugin',
|
||||
'server',
|
||||
'collection',
|
||||
] as const
|
||||
|
||||
export type OrderedProjectType = (typeof PROJECT_TYPE_ORDER)[number]
|
||||
|
||||
const PROJECT_TYPE_SORT_ALIASES: Record<string, string> = {
|
||||
minecraft_java_server: 'server',
|
||||
shaderpack: 'shader',
|
||||
}
|
||||
|
||||
export function getProjectTypeSortIndex(type: string): number {
|
||||
const normalized = PROJECT_TYPE_SORT_ALIASES[type] ?? type
|
||||
const index = (PROJECT_TYPE_ORDER as readonly string[]).indexOf(normalized)
|
||||
return index === -1 ? PROJECT_TYPE_ORDER.length : index
|
||||
}
|
||||
|
||||
export function compareProjectTypes(a: string, b: string): number {
|
||||
return getProjectTypeSortIndex(a) - getProjectTypeSortIndex(b)
|
||||
}
|
||||
|
||||
export function sortProjectTypes<T extends string>(types: Iterable<T>): T[] {
|
||||
return [...types].sort(compareProjectTypes)
|
||||
}
|
||||
@@ -93,10 +93,10 @@ export type ProjectType =
|
||||
|
||||
const ALL_PROJECT_TYPES: ProjectType[] = [
|
||||
'mod',
|
||||
'modpack',
|
||||
'resourcepack',
|
||||
'shader',
|
||||
'datapack',
|
||||
'shader',
|
||||
'modpack',
|
||||
'plugin',
|
||||
'server',
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user