feat: improve the "New group" jank

This commit is contained in:
tdgao
2026-07-30 15:46:12 -07:00
parent c063ca1ed5
commit 8f8e45a29d
4 changed files with 53 additions and 41 deletions
@@ -21,10 +21,10 @@
</ButtonStyled>
</div>
<div class="ml-auto flex items-center gap-0.5">
<ButtonStyled type="transparent">
<ButtonStyled v-if="displayState.group === 'Group'" type="transparent">
<button type="button" :disabled="busy" @click="createGroupFromSelection">
<PlusIcon />
<span class="bar-label">{{ formatMessage(messages.createGroup) }}</span>
<SquarePlusIcon />
<span class="bar-label">{{ formatMessage(messages.newGroup) }}</span>
</button>
</ButtonStyled>
<ButtonStyled v-if="selectedGroupedInstances.length > 0" type="transparent">
@@ -55,7 +55,7 @@
</template>
<script setup lang="ts">
import { MinusIcon, PlusIcon, TrashIcon } from '@modrinth/assets'
import { MinusIcon, SquarePlusIcon, TrashIcon } from '@modrinth/assets'
import {
ButtonStyled,
commonMessages,
@@ -80,6 +80,7 @@ const {
setSelectedLibraryInstances,
creatingGroup,
createDefaultGroup,
displayState,
} = useLibrary()
const confirmDeleteModal = ref<InstanceType<typeof ConfirmDeleteInstanceModal>>()
@@ -111,9 +112,9 @@ const messages = defineMessages({
id: 'app.library.selection.deleting',
defaultMessage: 'Deleting selected instances',
},
createGroup: {
id: 'app.library.selection.create-group',
defaultMessage: 'Create group',
newGroup: {
id: 'app.library.selection.new-group',
defaultMessage: 'New group',
},
removeFromGroup: {
id: 'app.library.selection.remove-from-group',
@@ -124,7 +125,9 @@ const messages = defineMessages({
async function createGroupFromSelection() {
if (busy.value) return
await createDefaultGroup(selectedInstanceIds.value)
const instanceIds = selectedInstanceIds.value
clearLibraryInstanceSelection()
await createDefaultGroup(instanceIds)
}
async function removeSelectedInstancesFromGroups() {
@@ -130,21 +130,29 @@ watch(selectedLibraryInstances, (selectedInstances) => {
<section data-library-page-background class="flex flex-col gap-3 pb-16">
<h2 class="m-0 text-2xl font-semibold text-contrast">Library</h2>
<LibraryToolbar />
<div data-library-page-background class="flex flex-col">
<InstanceGroup
v-for="instanceGroup in visibleInstanceGroups"
:key="instanceGroup.id"
:hide-header="instanceGroup.key === 'None' && visibleInstanceGroups.length === 1"
:instance-group="instanceGroup"
:selection-anchor-instance-id="
anchorInstance?.groupId === instanceGroup.id ? anchorInstance.instanceId : null
"
@toggle-selection="
(instanceId: string, shiftKey: boolean) =>
handleToggleInstance(instanceGroup.id, instanceId, shiftKey)
"
/>
</div>
<TransitionGroup
data-library-page-background
tag="div"
class="flex flex-col"
move-class="transition-transform duration-200 ease-out"
enter-active-class="transition-[opacity,transform] duration-200 ease-out"
enter-from-class="opacity-0 -translate-y-2"
enter-to-class="opacity-100 translate-y-0"
>
<div v-for="instanceGroup in visibleInstanceGroups" :key="instanceGroup.id" class="min-w-0">
<InstanceGroup
:hide-header="instanceGroup.key === 'None' && visibleInstanceGroups.length === 1"
:instance-group="instanceGroup"
:selection-anchor-instance-id="
anchorInstance?.groupId === instanceGroup.id ? anchorInstance.instanceId : null
"
@toggle-selection="
(instanceId: string, shiftKey: boolean) =>
handleToggleInstance(instanceGroup.id, instanceId, shiftKey)
"
/>
</div>
</TransitionGroup>
</section>
</InstanceGroupDnd>
<LibrarySelectionActionBar />
@@ -296,6 +296,7 @@ watch(
if (groupId !== pendingGroupId) return
await nextTick()
groupDropTarget.value?.scrollIntoView({ block: 'nearest' })
await groupNameInput.value?.startEditing()
completePendingGroupNameEdit(groupId)
},
@@ -94,11 +94,16 @@ function createLibraryState(instances: Ref<GameInstance[]>) {
const { formatMessage } = useVIntl()
const search = ref('')
const filters = ref<LibraryFilters>({
instanceType: [],
gameVersion: [],
loader: [],
})
const filters = useStorage<LibraryFilters>(
'Instances-grid-filters',
{
instanceType: [],
gameVersion: [],
loader: [],
},
localStorage,
{ mergeDefaults: true },
)
const serverProjectIds = ref(new Set<string>())
const libraryGroups = ref<InstanceGroupDefinition[]>([])
const isNewGroupModalOpen = ref(false)
@@ -146,6 +151,10 @@ function createLibraryState(instances: Ref<GameInstance[]>) {
displayState.value.sortBy = 'Name'
}
if (!libraryGroupOptions.some((option) => option.value === displayState.value.group)) {
displayState.value.group = 'Group'
}
const linkedInstances = computed(() => instances.value.filter((instance) => instance.link))
const collapsedSectionKeys = computed(() => new Set(displayState.value.collapsedGroups))
const groupNames = computed(
@@ -804,11 +813,6 @@ function createLibraryState(instances: Ref<GameInstance[]>) {
try {
const group = await createInstanceGroup(getDefaultNewGroupName())
libraryGroups.value = [
...libraryGroups.value.filter((existingGroup) => existingGroup.id !== group.id),
group,
]
const selectedInstanceIds = new Set(instanceIds)
const instancesToAdd = instances.value.filter((instance) =>
selectedInstanceIds.has(instance.id),
@@ -821,14 +825,10 @@ function createLibraryState(instances: Ref<GameInstance[]>) {
),
)
if (instancesToAdd.length > 0) {
setSelectedLibraryInstances(
instancesToAdd.map((instance) => ({
instanceId: instance.id,
groupId: group.id,
})),
)
}
libraryGroups.value = [
...libraryGroups.value.filter((existingGroup) => existingGroup.id !== group.id),
group,
]
displayState.value.group = 'Group'
groupIdPendingNameEdit.value = group.id