mirror of
https://github.com/modrinth/code.git
synced 2026-09-04 22:10:15 +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:
@@ -1,20 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ClipboardCopyIcon,
|
||||
EditIcon,
|
||||
EyeIcon,
|
||||
FolderOpenIcon,
|
||||
MinusIcon,
|
||||
PaletteIcon,
|
||||
PlayIcon,
|
||||
PlusIcon,
|
||||
StarIcon,
|
||||
StopCircleIcon,
|
||||
TrashIcon,
|
||||
UploadIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { computed, nextTick, onDeactivated, onUnmounted, ref, toRef, watch } from 'vue'
|
||||
import Draggable from 'vuedraggable'
|
||||
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import ContextMenu from '@/components/ui/context-menu/index.vue'
|
||||
import IconEditorModal from '@/components/ui/instance_settings/icon-editor-modal/index.vue'
|
||||
import GroupInstancesModal from '@/components/ui/library/group-instances-modal.vue'
|
||||
import InstanceGroup from '@/components/ui/library/instance-group/index.vue'
|
||||
import InstanceGroupDnd from '@/components/ui/library/instance-group/instance-group-dnd.vue'
|
||||
@@ -55,6 +59,30 @@ const messages = defineMessages({
|
||||
id: 'app.library.instance.action.view-instance',
|
||||
defaultMessage: 'View instance',
|
||||
},
|
||||
editIcon: {
|
||||
id: 'instance.settings.tabs.general.edit-icon',
|
||||
defaultMessage: 'Edit icon',
|
||||
},
|
||||
selectIcon: {
|
||||
id: 'instance.settings.tabs.general.edit-icon.select',
|
||||
defaultMessage: 'Select icon',
|
||||
},
|
||||
replaceIcon: {
|
||||
id: 'instance.settings.tabs.general.edit-icon.replace',
|
||||
defaultMessage: 'Replace icon',
|
||||
},
|
||||
createIcon: {
|
||||
id: 'instance.settings.tabs.general.edit-icon.create',
|
||||
defaultMessage: 'Create an icon',
|
||||
},
|
||||
editCreatedIcon: {
|
||||
id: 'instance.settings.tabs.general.edit-icon.edit-created',
|
||||
defaultMessage: 'Edit icon',
|
||||
},
|
||||
removeIcon: {
|
||||
id: 'instance.settings.tabs.general.edit-icon.remove',
|
||||
defaultMessage: 'Remove icon',
|
||||
},
|
||||
duplicateInstance: {
|
||||
id: 'app.library.instance.action.duplicate',
|
||||
defaultMessage: 'Duplicate instance',
|
||||
@@ -78,10 +106,13 @@ const {
|
||||
reorderGroups,
|
||||
instanceOptions,
|
||||
confirmDeleteModal,
|
||||
iconEditorModal,
|
||||
currentIconEditorInstance,
|
||||
currentDeleteInstances,
|
||||
clearLibraryInstanceSelection,
|
||||
deleteInstance,
|
||||
handleInstanceOption,
|
||||
handleInstanceIconSaved,
|
||||
selectedLibraryInstances,
|
||||
setSelectedLibraryInstances,
|
||||
toggleLibraryInstanceSelection,
|
||||
@@ -100,40 +131,35 @@ const visibleInstanceGroups = computed(() =>
|
||||
),
|
||||
)
|
||||
|
||||
const visibleCustomGroups = computed(() =>
|
||||
const visibleReorderableGroups = computed(() =>
|
||||
displayState.value.group === 'Group'
|
||||
? visibleInstanceGroups.value.filter(
|
||||
(group) => group.id !== FAVORITES_GROUP_ID && group.id !== 'group:none',
|
||||
)
|
||||
? visibleInstanceGroups.value.filter((group) => group.id !== FAVORITES_GROUP_ID)
|
||||
: [],
|
||||
)
|
||||
const visibleFavoritesGroup = computed(() =>
|
||||
visibleInstanceGroups.value.find((group) => group.id === FAVORITES_GROUP_ID),
|
||||
)
|
||||
const visibleUngroupedGroup = computed(() =>
|
||||
visibleInstanceGroups.value.find((group) => group.id === 'group:none'),
|
||||
)
|
||||
const draggableCustomGroups = ref<InstanceGroupType[]>([])
|
||||
const draggableGroups = ref<InstanceGroupType[]>([])
|
||||
const libraryGroupsContainer = ref<HTMLElement>()
|
||||
const isDraggingGroup = ref(false)
|
||||
const GROUP_REORDERING_CLASS = 'instance-group-reordering'
|
||||
const canDragReorderGroups = computed(
|
||||
() => !reorderingGroups.value && draggableCustomGroups.value.length > 1,
|
||||
() => !reorderingGroups.value && draggableGroups.value.length > 1,
|
||||
)
|
||||
|
||||
watch(
|
||||
visibleCustomGroups,
|
||||
visibleReorderableGroups,
|
||||
(groups) => {
|
||||
if (!isDraggingGroup.value) {
|
||||
const previousGroupTops = getCustomGroupTops()
|
||||
draggableCustomGroups.value = [...groups]
|
||||
void nextTick(() => animateCustomGroupReorder(previousGroupTops))
|
||||
const previousGroupTops = getReorderableGroupTops()
|
||||
draggableGroups.value = [...groups]
|
||||
void nextTick(() => animateGroupReorder(previousGroupTops))
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function getCustomGroupTops() {
|
||||
function getReorderableGroupTops() {
|
||||
const groupTops = new Map<string, number>()
|
||||
const groupElements = libraryGroupsContainer.value?.querySelectorAll<HTMLElement>(
|
||||
'[data-instance-group-reorder-id]',
|
||||
@@ -149,7 +175,7 @@ function getCustomGroupTops() {
|
||||
return groupTops
|
||||
}
|
||||
|
||||
function animateCustomGroupReorder(previousGroupTops: Map<string, number>) {
|
||||
function animateGroupReorder(previousGroupTops: Map<string, number>) {
|
||||
if (
|
||||
previousGroupTops.size === 0 ||
|
||||
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
@@ -185,10 +211,10 @@ function onGroupDragEnd() {
|
||||
isDraggingGroup.value = false
|
||||
document.documentElement.classList.remove(GROUP_REORDERING_CLASS)
|
||||
|
||||
const currentGroupIds = visibleCustomGroups.value.map((group) => group.id)
|
||||
const orderedGroupIds = draggableCustomGroups.value.map((group) => group.id)
|
||||
const currentGroupIds = visibleReorderableGroups.value.map((group) => group.id)
|
||||
const orderedGroupIds = draggableGroups.value.map((group) => group.id)
|
||||
if (orderedGroupIds.every((groupId, index) => groupId === currentGroupIds[index])) {
|
||||
draggableCustomGroups.value = [...visibleCustomGroups.value]
|
||||
draggableGroups.value = [...visibleReorderableGroups.value]
|
||||
return
|
||||
}
|
||||
|
||||
@@ -259,6 +285,10 @@ function setConfirmDeleteModal(component: unknown) {
|
||||
confirmDeleteModal.value = component as InstanceType<typeof ConfirmDeleteInstanceModal> | null
|
||||
}
|
||||
|
||||
function setIconEditorModal(component: unknown) {
|
||||
iconEditorModal.value = component as InstanceType<typeof IconEditorModal> | null
|
||||
}
|
||||
|
||||
watch(selectedLibraryInstances, (selectedInstances) => {
|
||||
if (selectedInstances.size === 0) {
|
||||
anchorInstance.value = null
|
||||
@@ -313,7 +343,7 @@ watch(selectedLibraryInstances, (selectedInstances) => {
|
||||
</div>
|
||||
|
||||
<Draggable
|
||||
:list="draggableCustomGroups"
|
||||
:list="draggableGroups"
|
||||
class="flex flex-col"
|
||||
item-key="id"
|
||||
:disabled="!canDragReorderGroups"
|
||||
@@ -341,6 +371,7 @@ watch(selectedLibraryInstances, (selectedInstances) => {
|
||||
>
|
||||
<InstanceGroup
|
||||
:can-drag-reorder="canDragReorderGroups"
|
||||
:hide-header="visibleInstanceGroups.length === 1"
|
||||
:instance-group="instanceGroup"
|
||||
:selection-anchor-instance-id="
|
||||
anchorInstance?.groupId === instanceGroup.id ? anchorInstance?.instanceId : null
|
||||
@@ -353,20 +384,6 @@ watch(selectedLibraryInstances, (selectedInstances) => {
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
|
||||
<div v-if="visibleUngroupedGroup" class="min-w-0">
|
||||
<InstanceGroup
|
||||
:hide-header="visibleInstanceGroups.length === 1"
|
||||
:instance-group="visibleUngroupedGroup"
|
||||
:selection-anchor-instance-id="
|
||||
anchorInstance?.groupId === 'group:none' ? anchorInstance.instanceId : null
|
||||
"
|
||||
@toggle-selection="
|
||||
(instanceId: string, shiftKey: boolean) =>
|
||||
handleToggleInstance('group:none', instanceId, shiftKey)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TransitionGroup
|
||||
@@ -407,6 +424,12 @@ watch(selectedLibraryInstances, (selectedInstances) => {
|
||||
:instances="currentDeleteInstances"
|
||||
@delete="deleteInstance"
|
||||
/>
|
||||
<IconEditorModal
|
||||
:ref="setIconEditorModal"
|
||||
:instance-id="currentIconEditorInstance?.id"
|
||||
:config="currentIconEditorInstance?.icon_config"
|
||||
@saved="handleInstanceIconSaved"
|
||||
/>
|
||||
<ContextMenu :ref="setInstanceOptions" @option-clicked="handleInstanceOption">
|
||||
<template #play> <PlayIcon /> {{ formatMessage(messages.play) }} </template>
|
||||
<template #stop> <StopCircleIcon /> {{ formatMessage(messages.stop) }} </template>
|
||||
@@ -419,6 +442,14 @@ watch(selectedLibraryInstances, (selectedInstances) => {
|
||||
</template>
|
||||
<template #add_content> <PlusIcon /> {{ formatMessage(messages.addContent) }} </template>
|
||||
<template #edit> <EyeIcon /> {{ formatMessage(messages.viewInstance) }} </template>
|
||||
<template #edit_icon> <EditIcon /> {{ formatMessage(messages.editIcon) }} </template>
|
||||
<template #select_icon> <UploadIcon /> {{ formatMessage(messages.selectIcon) }} </template>
|
||||
<template #replace_icon> <UploadIcon /> {{ formatMessage(messages.replaceIcon) }} </template>
|
||||
<template #create_icon> <PaletteIcon /> {{ formatMessage(messages.createIcon) }} </template>
|
||||
<template #edit_created_icon>
|
||||
<PaletteIcon /> {{ formatMessage(messages.editCreatedIcon) }}
|
||||
</template>
|
||||
<template #remove_icon> <TrashIcon /> {{ formatMessage(messages.removeIcon) }} </template>
|
||||
<template #duplicate>
|
||||
<ClipboardCopyIcon /> {{ formatMessage(messages.duplicateInstance) }}
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user