mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 03:55:59 +00:00
fix: app library post release (#7211)
* fix: fixed height on modal * fix: conditional auto padding being applied on user avatars * feat: allow ungrouped to be dragged * feat: add drag handle to resize num items in jump in * qa * feat: add context menu submenus * feat: hook up icon editing in sub menu * fix: breadcrumb and sidebar not matching * fix: bring back signal icon for ping and players online * feat: add compact mode for library * smaller checkmark * pnpm prepr * feat: bring back loader/game version sort * add loader symbols * refactor: compact mode with sync'd app settings * pnpm prepr * move loaders up * qa * feat: exclude loaders from randomizing
This commit is contained in:
+21
-10
@@ -2,16 +2,25 @@
|
||||
import { ArrowDownIcon, ArrowUpIcon, EditIcon, SquarePlusIcon, TrashIcon } from '@modrinth/assets'
|
||||
import { defineMessages, IconButton, useVIntl } from '@modrinth/ui'
|
||||
|
||||
defineProps<{
|
||||
deleting?: boolean
|
||||
canMoveDown: boolean
|
||||
canMoveUp: boolean
|
||||
onAddToGroup: () => void
|
||||
onDeleteGroup: () => void
|
||||
onEditGroupName: () => void
|
||||
onMoveDown: () => void
|
||||
onMoveUp: () => void
|
||||
}>()
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
deleting?: boolean
|
||||
canMoveDown: boolean
|
||||
canMoveUp: boolean
|
||||
onAddToGroup: () => void
|
||||
onDeleteGroup: () => void
|
||||
onEditGroupName: () => void
|
||||
onMoveDown: () => void
|
||||
onMoveUp: () => void
|
||||
showDelete?: boolean
|
||||
showEdit?: boolean
|
||||
}>(),
|
||||
{
|
||||
deleting: false,
|
||||
showDelete: true,
|
||||
showEdit: true,
|
||||
},
|
||||
)
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
@@ -64,6 +73,7 @@ const messages = defineMessages({
|
||||
<ArrowDownIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
v-if="showEdit"
|
||||
v-tooltip="formatMessage(messages.editGroupName)"
|
||||
:label="formatMessage(messages.editGroupName)"
|
||||
type="quiet"
|
||||
@@ -82,6 +92,7 @@ const messages = defineMessages({
|
||||
<SquarePlusIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
v-if="showDelete"
|
||||
v-tooltip="formatMessage(messages.deleteGroup)"
|
||||
:label="formatMessage(messages.deleteGroup)"
|
||||
type="quiet"
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from '@modrinth/ui'
|
||||
import { computed, inject, nextTick, onActivated, onDeactivated, onMounted, ref, watch } from 'vue'
|
||||
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import ContextMenu from '@/components/ui/context-menu/index.vue'
|
||||
import GroupActionButtons from '@/components/ui/library/instance-group/group-action-buttons.vue'
|
||||
import InstanceCard from '@/components/ui/library/instance-group/instance-card.vue'
|
||||
import type {
|
||||
@@ -29,6 +29,7 @@ import type {
|
||||
InstanceGroup as InstanceGroupType,
|
||||
} from '@/components/ui/library/use-library'
|
||||
import { useLibrary } from '@/components/ui/library/use-library'
|
||||
import { useAppSettings } from '@/composables/use-app-settings.ts'
|
||||
import { FAVORITES_GROUP_ID, MAX_INSTANCE_GROUP_NAME_LENGTH } from '@/helpers/instance-groups'
|
||||
|
||||
const INSTANCE_GRID_OBSERVER_ACTIVATION_DELAY = 500
|
||||
@@ -48,6 +49,8 @@ const props = withDefaults(
|
||||
)
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const appSettings = useAppSettings()
|
||||
const compactMode = computed(() => appSettings.getFeatureFlag('compact_instance_cards'))
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const {
|
||||
isSectionCollapsed,
|
||||
@@ -84,6 +87,9 @@ const isFavorites = computed(() => props.instanceGroup.id === FAVORITES_GROUP_ID
|
||||
const isCustomGroup = computed(
|
||||
() => displayState.value.group === 'Group' && !isUngrouped.value && !isFavorites.value,
|
||||
)
|
||||
const isReorderableGroup = computed(
|
||||
() => displayState.value.group === 'Group' && !isFavorites.value,
|
||||
)
|
||||
const groupContextMenuOpen = ref(false)
|
||||
const isGroupToggleBlocked = computed(
|
||||
() => isSearching.value || groupContextMenuOpen.value || Boolean(groupNameInput.value?.isEditing),
|
||||
@@ -383,7 +389,7 @@ onMounted(startInstanceGridResizeObserver)
|
||||
v-if="!hideHeader"
|
||||
class="group/header h-10 flex w-full items-center gap-2 border-0 border-b border-solid border-b-surface-5"
|
||||
:class="{
|
||||
'instance-group-reorder-handle': isCustomGroup && canDragReorder,
|
||||
'instance-group-reorder-handle': isReorderableGroup && canDragReorder,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
@@ -442,7 +448,7 @@ onMounted(startInstanceGridResizeObserver)
|
||||
</div>
|
||||
<div class="min-w-0 flex-1" />
|
||||
<GroupActionButtons
|
||||
v-if="isCustomGroup"
|
||||
v-if="isCustomGroup || isUngrouped"
|
||||
:can-move-down="canMoveGroupDown(instanceGroup.id)"
|
||||
:can-move-up="canMoveGroupUp(instanceGroup.id)"
|
||||
:deleting="deletingGroup"
|
||||
@@ -451,6 +457,8 @@ onMounted(startInstanceGridResizeObserver)
|
||||
:on-edit-group-name="() => groupNameInput?.startEditing()"
|
||||
:on-move-down="() => moveGroup(instanceGroup.id, 1)"
|
||||
:on-move-up="() => moveGroup(instanceGroup.id, -1)"
|
||||
:show-delete="!isUngrouped"
|
||||
:show-edit="!isUngrouped"
|
||||
/>
|
||||
</div>
|
||||
<Accordion
|
||||
@@ -470,7 +478,12 @@ onMounted(startInstanceGridResizeObserver)
|
||||
<div ref="instanceGridContent">
|
||||
<TransitionGroup
|
||||
tag="section"
|
||||
class="grid min-h-[45px] w-full grid-cols-[repeat(auto-fill,minmax(min(10rem,100%),1fr))] max-xl:grid-cols-[repeat(auto-fill,minmax(min(8rem,100%),1fr))] gap-3 overflow-y-auto scroll-smooth"
|
||||
class="grid min-h-[45px] w-full gap-3 overflow-y-auto scroll-smooth"
|
||||
:class="
|
||||
compactMode
|
||||
? 'grid-cols-[repeat(auto-fill,minmax(min(15rem,100%),1fr))]'
|
||||
: 'grid-cols-[repeat(auto-fill,minmax(min(10rem,100%),1fr))] max-xl:grid-cols-[repeat(auto-fill,minmax(min(8rem,100%),1fr))]'
|
||||
"
|
||||
move-class="transition-transform duration-200 ease-out motion-reduce:transition-none"
|
||||
enter-active-class="transition-[opacity,transform] duration-[150ms] ease-out motion-reduce:transition-none"
|
||||
enter-from-class="opacity-0"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Avatar, truncatedTooltip } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { useAppSettings } from '@/composables/use-app-settings.ts'
|
||||
import { getInstanceIconUrl } from '@/helpers/instance'
|
||||
import type { GameInstance } from '@/helpers/types'
|
||||
|
||||
@@ -16,6 +17,8 @@ const props = withDefaults(
|
||||
)
|
||||
|
||||
const iconSrc = computed(() => getInstanceIconUrl(props.instance.icon_path))
|
||||
const appSettings = useAppSettings()
|
||||
const compactMode = computed(() => appSettings.getFeatureFlag('compact_instance_cards'))
|
||||
|
||||
const nameRef = ref<HTMLElement | null>(null)
|
||||
const versionRef = ref<HTMLElement | null>(null)
|
||||
@@ -23,30 +26,40 @@ const versionRef = ref<HTMLElement | null>(null)
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="relative flex w-full min-w-0 select-none flex-col items-start justify-end gap-3 overflow-clip rounded-[20px] border border-solid bg-surface-3 p-3 text-left transition-all"
|
||||
class="relative flex w-full min-w-0 select-none overflow-clip border border-solid bg-surface-3 text-left transition-all"
|
||||
:class="{
|
||||
'flex-row items-center justify-start gap-2.5 rounded-xl p-2.5': compactMode,
|
||||
'flex-col items-start justify-end gap-3 rounded-[20px] p-3': !compactMode,
|
||||
'[border-color:color-mix(in_srgb,var(--color-text-primary)_40%,transparent)] brightness-110':
|
||||
selected,
|
||||
'border-surface-4': !selected,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="relative flex aspect-square min-w-full shrink-0 items-center overflow-clip rounded-2xl"
|
||||
class="relative flex shrink-0 items-center overflow-clip"
|
||||
:class="compactMode ? 'size-10 rounded-lg' : 'aspect-square min-w-full rounded-2xl'"
|
||||
>
|
||||
<Avatar
|
||||
class="pointer-events-none !rounded-2xl outline-none"
|
||||
class="pointer-events-none outline-none"
|
||||
:class="compactMode ? '!rounded-lg' : '!rounded-2xl'"
|
||||
size="100%"
|
||||
:src="iconSrc"
|
||||
:tint-by="instance.id"
|
||||
alt=""
|
||||
no-shadow
|
||||
/>
|
||||
<slot name="loading" />
|
||||
<div class="absolute bottom-1.5 right-1.5 z-[1] flex size-12 items-center justify-center">
|
||||
<slot name="leading" />
|
||||
<slot name="loading" :compact="compactMode" />
|
||||
<div
|
||||
class="absolute z-[1] flex items-center justify-center"
|
||||
:class="compactMode ? 'inset-0' : 'bottom-1.5 right-1.5 size-12'"
|
||||
>
|
||||
<slot name="leading" :compact="compactMode" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex min-w-0 w-full flex-col items-start justify-center gap-1 px-0.5">
|
||||
<div
|
||||
class="flex min-w-0 w-full flex-col items-start justify-center gap-1 px-0.5"
|
||||
:class="{ 'pr-10': compactMode }"
|
||||
>
|
||||
<p
|
||||
ref="nameRef"
|
||||
v-tooltip="truncatedTooltip(nameRef, instance.name)"
|
||||
@@ -62,6 +75,6 @@ const versionRef = ref<HTMLElement | null>(null)
|
||||
{{ instance.loader }} {{ instance.game_version }}
|
||||
</p>
|
||||
</div>
|
||||
<slot name="overlay" />
|
||||
<slot name="overlay" :compact="compactMode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -285,7 +285,7 @@ onMounted(() => {
|
||||
@mouseenter="checkProcess"
|
||||
@pointerdown="handlePointerDown"
|
||||
>
|
||||
<template #loading>
|
||||
<template #loading="{ compact }">
|
||||
<div
|
||||
v-if="loadingIndicatorVisible"
|
||||
class="pointer-events-none absolute inset-0 z-[1] flex items-center justify-center"
|
||||
@@ -293,13 +293,17 @@ onMounted(() => {
|
||||
<div class="absolute inset-0 bg-surface-1 opacity-30" />
|
||||
<SpinnerIcon
|
||||
v-tooltip="formatMessage(modLoading ? messages.loading : messages.installing)"
|
||||
class="relative size-[30%] animate-spin text-contrast"
|
||||
class="relative animate-spin text-contrast"
|
||||
:class="compact ? 'size-5' : 'size-[30%]'"
|
||||
tabindex="-1"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #leading>
|
||||
<div class="relative flex size-12 shrink-0 items-center justify-center">
|
||||
<template #leading="{ compact }">
|
||||
<div
|
||||
class="relative flex shrink-0 items-center justify-center"
|
||||
:class="compact ? 'size-10' : 'size-12'"
|
||||
>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<IconButton
|
||||
v-if="playing"
|
||||
@@ -307,7 +311,7 @@ onMounted(() => {
|
||||
:label="formatMessage(messages.stop)"
|
||||
type="colored"
|
||||
color="red"
|
||||
size="lg"
|
||||
:size="compact ? 'md' : 'lg'"
|
||||
@click="(e) => stop(e, 'InstanceCard')"
|
||||
@mouseenter="checkProcess"
|
||||
>
|
||||
@@ -325,7 +329,7 @@ onMounted(() => {
|
||||
:label="formatMessage(messages.repair)"
|
||||
type="colored"
|
||||
color="brand"
|
||||
size="lg"
|
||||
:size="compact ? 'md' : 'lg'"
|
||||
class="origin-bottom scale-75 opacity-0 transition-opacity group-hover/card:scale-100 group-hover/card:opacity-100"
|
||||
@click="(e) => repair(e)"
|
||||
>
|
||||
@@ -342,7 +346,7 @@ onMounted(() => {
|
||||
:label="formatMessage(messages.play)"
|
||||
type="colored"
|
||||
color="brand"
|
||||
size="lg"
|
||||
:size="compact ? 'md' : 'lg'"
|
||||
class="origin-bottom scale-75 opacity-0 transition-opacity group-hover/card:scale-100 group-hover/card:opacity-100"
|
||||
@click="(e) => play(e, 'InstanceCard')"
|
||||
@mouseenter="checkProcess"
|
||||
@@ -352,18 +356,21 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #overlay>
|
||||
<template #overlay="{ compact }">
|
||||
<button
|
||||
type="button"
|
||||
class="selection-button group/selection absolute right-2 top-1.5 z-[2] flex size-[50px] cursor-pointer items-start pt-4 justify-center border-0 bg-transparent p-0"
|
||||
class="selection-button group/selection absolute z-[2] flex size-[50px] cursor-pointer items-start pt-4 justify-center border-0 bg-transparent p-0"
|
||||
:class="compact ? '-right-1 -top-1' : 'right-2 top-1.5'"
|
||||
:aria-label="formatMessage(selected ? messages.deselect : messages.select)"
|
||||
:aria-pressed="selected"
|
||||
@click.stop="toggleSelection"
|
||||
>
|
||||
<span
|
||||
v-tooltip="formatMessage(selected ? messages.deselect : messages.select)"
|
||||
class="relative flex size-[24px] items-center justify-center rounded-full opacity-0 transition-opacity duration-200 ease-out group-hover/card:opacity-100 group-hover/selection:brightness-125"
|
||||
class="relative flex items-center justify-center rounded-full opacity-0 transition-opacity duration-200 ease-out group-hover/card:opacity-100 group-hover/selection:brightness-125"
|
||||
:class="{
|
||||
'size-[20px]': compact,
|
||||
'size-[24px]': !compact,
|
||||
'border-0 !opacity-100': selected,
|
||||
'border-2 border-solid border-primary bg-transparent': !selected,
|
||||
'[outline:3px_solid_var(--color-purple)] outline-offset-1':
|
||||
|
||||
Reference in New Issue
Block a user