feat: better tooltips for mods in content tab hosting panel (#5679)
* feat: better tooltips for mods in content tab hosting panel * feat: qa
This commit is contained in:
@@ -25,7 +25,12 @@ import { useVIntl } from '#ui/composables/i18n'
|
||||
import { commonMessages } from '#ui/utils/common-messages'
|
||||
import { truncatedTooltip } from '#ui/utils/truncate'
|
||||
|
||||
import type { ContentCardProject, ContentCardVersion, ContentOwner } from '../types'
|
||||
import type {
|
||||
ClientWarningType,
|
||||
ContentCardProject,
|
||||
ContentCardVersion,
|
||||
ContentOwner,
|
||||
} from '../types'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
@@ -39,6 +44,8 @@ interface Props {
|
||||
installing?: boolean
|
||||
hasUpdate?: boolean
|
||||
isClientOnly?: boolean
|
||||
clientWarning?: ClientWarningType | null
|
||||
hideSwitchVersion?: boolean
|
||||
overflowOptions?: OverflowMenuOption[]
|
||||
disabled?: boolean
|
||||
showCheckbox?: boolean
|
||||
@@ -55,6 +62,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
installing: false,
|
||||
hasUpdate: false,
|
||||
isClientOnly: false,
|
||||
clientWarning: null,
|
||||
hideSwitchVersion: false,
|
||||
overflowOptions: undefined,
|
||||
disabled: false,
|
||||
showCheckbox: false,
|
||||
@@ -83,6 +92,17 @@ const fileNameRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const isDisabled = computed(() => props.disabled || props.installing)
|
||||
|
||||
const clientWarningMessage = computed(() => {
|
||||
switch (props.clientWarning) {
|
||||
case 'retained':
|
||||
return commonMessages.clientRetainedWarning
|
||||
case 'depends':
|
||||
return commonMessages.clientDependsWarning
|
||||
default:
|
||||
return commonMessages.clientOnlyWarning
|
||||
}
|
||||
})
|
||||
|
||||
const { shift: shiftHeld } = useMagicKeys()
|
||||
const deleteHovered = ref(false)
|
||||
</script>
|
||||
@@ -147,7 +167,7 @@ const deleteHovered = ref(false)
|
||||
<TriangleAlertIcon class="size-4 shrink-0 text-orange" />
|
||||
<template #popper>
|
||||
<div class="max-w-[18rem] text-sm">
|
||||
{{ formatMessage(commonMessages.clientOnlyWarning) }}
|
||||
{{ formatMessage(clientWarningMessage) }}
|
||||
</div>
|
||||
</template>
|
||||
</Tooltip>
|
||||
@@ -260,7 +280,11 @@ const deleteHovered = ref(false)
|
||||
<DownloadIcon class="size-5" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else-if="hasSwitchVersionListener && version" circular type="transparent">
|
||||
<ButtonStyled
|
||||
v-else-if="hasSwitchVersionListener && version && !hideSwitchVersion"
|
||||
circular
|
||||
type="transparent"
|
||||
>
|
||||
<button
|
||||
v-tooltip="formatMessage(commonMessages.switchVersionButton)"
|
||||
:disabled="isDisabled"
|
||||
|
||||
@@ -276,6 +276,8 @@ function handleSort(column: ContentCardTableSortColumn) {
|
||||
:installing="item.installing"
|
||||
:has-update="item.hasUpdate"
|
||||
:is-client-only="item.isClientOnly"
|
||||
:client-warning="item.clientWarning"
|
||||
:hide-switch-version="item.hideSwitchVersion"
|
||||
:overflow-options="item.overflowOptions"
|
||||
:disabled="item.disabled"
|
||||
:show-checkbox="showSelection"
|
||||
@@ -329,6 +331,8 @@ function handleSort(column: ContentCardTableSortColumn) {
|
||||
:enabled="item.enabled"
|
||||
:installing="item.installing"
|
||||
:has-update="item.hasUpdate"
|
||||
:is-client-only="item.isClientOnly"
|
||||
:client-warning="item.clientWarning"
|
||||
:overflow-options="item.overflowOptions"
|
||||
:disabled="item.disabled"
|
||||
:show-checkbox="showSelection"
|
||||
|
||||
@@ -138,19 +138,28 @@ onUnmounted(() => {
|
||||
class="@container flex flex-col gap-4 rounded-[20px] bg-bg-raised p-6 shadow-md"
|
||||
:class="{ 'opacity-50': disabled }"
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div class="flex min-w-0 flex-1 items-start gap-4">
|
||||
<div class="flex flex-wrap items-center justify-between gap-4">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-4">
|
||||
<AutoLink :to="projectLink" class="shrink-0">
|
||||
<Avatar :src="project.icon_url" :alt="project.title" size="5rem" no-shadow raised />
|
||||
</AutoLink>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<AutoLink
|
||||
:to="projectLink"
|
||||
class="text-xl font-semibold leading-8 text-contrast hover:underline"
|
||||
<div class="flex min-w-0 flex-col gap-1.5">
|
||||
<div class="flex min-w-0 flex-col">
|
||||
<AutoLink
|
||||
:to="projectLink"
|
||||
class="truncate text-xl font-semibold text-contrast"
|
||||
:class="projectLink ? 'hover:underline' : ''"
|
||||
>
|
||||
{{ project.title }}
|
||||
</AutoLink>
|
||||
<span v-if="project.filename" class="truncate text-secondary mb-2">
|
||||
{{ project.filename }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="owner || version"
|
||||
class="flex flex-nowrap items-center gap-2 overflow-hidden text-secondary"
|
||||
>
|
||||
{{ project.title }}
|
||||
</AutoLink>
|
||||
<div class="flex flex-nowrap items-center gap-2 overflow-hidden text-secondary">
|
||||
<AutoLink
|
||||
v-if="owner"
|
||||
:to="owner.link"
|
||||
@@ -346,13 +355,16 @@ onUnmounted(() => {
|
||||
{{ project.description }}
|
||||
</span>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<div v-if="project.downloads !== undefined" class="flex items-center gap-2 text-secondary">
|
||||
<div
|
||||
v-if="project.downloads != null || project.followers != null || categories?.length"
|
||||
class="flex flex-wrap items-center gap-3"
|
||||
>
|
||||
<div v-if="project.downloads != null" class="flex items-center gap-2 text-secondary">
|
||||
<DownloadIcon class="size-5" />
|
||||
<span class="font-medium">{{ formatCompact(project.downloads) }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="project.followers !== undefined" class="flex items-center gap-2 text-secondary">
|
||||
<div v-if="project.followers != null" class="flex items-center gap-2 text-secondary">
|
||||
<HeartIcon class="size-5" />
|
||||
<span class="font-medium">{{ formatCompact(project.followers) }}</span>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
normalizeProjectType,
|
||||
} from '#ui/utils/common-messages'
|
||||
|
||||
import { isClientOnlyEnvironment } from '../../composables/content-filtering'
|
||||
import { getClientWarningType, isClientOnlyEnvironment } from '../../composables/content-filtering'
|
||||
import type { ContentCardTableItem, ContentItem } from '../../types'
|
||||
import ContentCardTable from '../ContentCardTable.vue'
|
||||
import ContentSelectionBar from '../ContentSelectionBar.vue'
|
||||
@@ -239,7 +239,11 @@ const tableItems = computed<ContentCardTableItem[]>(() =>
|
||||
}
|
||||
: undefined,
|
||||
...(props.enableToggle ? { enabled: item.enabled } : {}),
|
||||
isClientOnly: isClientOnlyEnvironment(item.environment),
|
||||
isClientOnly:
|
||||
isClientOnlyEnvironment(item.environment) ||
|
||||
!!item.pack_client_retained ||
|
||||
!!item.pack_client_depends,
|
||||
clientWarning: getClientWarningType(item),
|
||||
disabled: disabledIds.value.has(item.file_name),
|
||||
overflowOptions: [
|
||||
...(props.switchVersion
|
||||
|
||||
@@ -5,7 +5,7 @@ import { computed, ref, watch } from 'vue'
|
||||
import { useVIntl } from '#ui/composables/i18n'
|
||||
import { commonProjectTypeCategoryMessages, normalizeProjectType } from '#ui/utils/common-messages'
|
||||
|
||||
import type { ContentItem } from '../types'
|
||||
import type { ClientWarningType, ContentItem } from '../types'
|
||||
|
||||
const CLIENT_ONLY_ENVIRONMENTS = new Set(['client_only', 'singleplayer_only'])
|
||||
|
||||
@@ -13,6 +13,13 @@ export function isClientOnlyEnvironment(env?: string | null): boolean {
|
||||
return !!env && CLIENT_ONLY_ENVIRONMENTS.has(env)
|
||||
}
|
||||
|
||||
export function getClientWarningType(item: ContentItem): ClientWarningType | null {
|
||||
if (item.pack_client_retained) return 'retained'
|
||||
if (item.pack_client_depends) return 'depends'
|
||||
if (isClientOnlyEnvironment(item.environment)) return 'environment'
|
||||
return null
|
||||
}
|
||||
|
||||
export interface ContentFilterOption {
|
||||
id: string
|
||||
label: string
|
||||
@@ -55,10 +62,7 @@ export function useContentFilters(items: Ref<ContentItem[]>, config?: ContentFil
|
||||
options.push({ id: 'updates', label: 'Updates' })
|
||||
}
|
||||
|
||||
if (
|
||||
config?.showClientOnlyFilter &&
|
||||
items.value.some((m) => isClientOnlyEnvironment(m.environment))
|
||||
) {
|
||||
if (config?.showClientOnlyFilter && items.value.some((m) => getClientWarningType(m) !== null)) {
|
||||
options.push({ id: 'client-only', label: 'Client-only' })
|
||||
}
|
||||
|
||||
@@ -102,7 +106,7 @@ export function useContentFilters(items: Ref<ContentItem[]>, config?: ContentFil
|
||||
for (const filter of activeAttributes) {
|
||||
if (filter === 'updates' && !item.has_update) return false
|
||||
if (filter === 'disabled' && item.enabled) return false
|
||||
if (filter === 'client-only' && !isClientOnlyEnvironment(item.environment)) return false
|
||||
if (filter === 'client-only' && getClientWarningType(item) === null) return false
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@@ -40,6 +40,7 @@ import ConfirmBulkUpdateModal from './components/modals/ConfirmBulkUpdateModal.v
|
||||
import ConfirmDeletionModal from './components/modals/ConfirmDeletionModal.vue'
|
||||
import ConfirmUnlinkModal from './components/modals/ConfirmUnlinkModal.vue'
|
||||
import {
|
||||
getClientWarningType,
|
||||
isClientOnlyEnvironment,
|
||||
useBulkOperation,
|
||||
useChangingItems,
|
||||
@@ -279,7 +280,12 @@ const tableItems = computed<ContentCardTableItem[]>(() => {
|
||||
item.installing === true,
|
||||
installing: item.installing === true,
|
||||
hasUpdate: item.has_update,
|
||||
isClientOnly: isClientOnlyEnvironment(item.environment),
|
||||
isClientOnly:
|
||||
isClientOnlyEnvironment(item.environment) ||
|
||||
!!item.pack_client_retained ||
|
||||
!!item.pack_client_depends,
|
||||
clientWarning: getClientWarningType(item),
|
||||
hideSwitchVersion: !base.versionLink,
|
||||
overflowOptions: ctx.getOverflowOptions?.(item),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -21,6 +21,8 @@ export interface ContentOwner {
|
||||
link?: string | RouteLocationRaw | (() => void)
|
||||
}
|
||||
|
||||
export type ClientWarningType = 'retained' | 'depends' | 'environment'
|
||||
|
||||
export interface ContentCardTableItem {
|
||||
id: string
|
||||
project: ContentCardProject
|
||||
@@ -33,6 +35,8 @@ export interface ContentCardTableItem {
|
||||
installing?: boolean
|
||||
hasUpdate?: boolean
|
||||
isClientOnly?: boolean
|
||||
clientWarning?: ClientWarningType | null
|
||||
hideSwitchVersion?: boolean
|
||||
overflowOptions?: OverflowMenuOption[]
|
||||
}
|
||||
|
||||
@@ -53,13 +57,19 @@ export interface ContentItem extends Omit<
|
||||
update_version_id: string | null
|
||||
date_added?: string
|
||||
environment?: string
|
||||
pack_client_retained?: boolean
|
||||
pack_client_depends?: boolean
|
||||
installing?: boolean
|
||||
}
|
||||
|
||||
export type ContentModpackCardProject = Pick<
|
||||
Labrinth.Projects.v2.Project,
|
||||
'id' | 'slug' | 'title' | 'icon_url' | 'description' | 'downloads' | 'followers'
|
||||
>
|
||||
'id' | 'slug' | 'title' | 'icon_url' | 'description'
|
||||
> & {
|
||||
downloads?: number | null
|
||||
followers?: number | null
|
||||
filename?: string | null
|
||||
}
|
||||
|
||||
export type ContentModpackCardVersion = Pick<
|
||||
Labrinth.Versions.v2.Version,
|
||||
|
||||
Reference in New Issue
Block a user