This commit is contained in:
Calum H. (IMB11)
2026-08-24 12:37:53 +01:00
parent 0fbd431169
commit 08901e725f
5 changed files with 34 additions and 101 deletions
@@ -163,11 +163,12 @@ const installTooltip = computed(() => {
<template>
<div
role="row"
class="flex items-center justify-between"
class="flex items-center"
:class="{
'h-[74px] gap-4 px-3': !inline && !enabledFor,
'h-[72px] gap-4 px-3': !inline && enabledFor,
'h-[72px] gap-2 px-3': !inline && enabledFor,
'gap-3': inline,
'justify-between': !enabledFor,
'opacity-50 grayscale': disabled && !installing,
'opacity-50': installing || (enabledFor && isEnabledForDisabled && !disabled),
}"
@@ -328,7 +329,7 @@ const installTooltip = computed(() => {
hideActions
? 'flex-1'
: enabledFor
? 'w-[250px] min-w-0 shrink-0'
? 'min-w-0 flex-1'
: 'flex-1 min-w-0',
!enabledFor && enabled === false && !disabled ? 'grayscale opacity-50' : '',
]"
@@ -27,10 +27,6 @@ const messages = defineMessages({
defaultMessage:
'Choose whether this content runs on the server, is sent to players, or is disabled in both places.',
},
sortEnabledFor: {
id: 'content.enabled-for.sort',
defaultMessage: 'Sort by where content is enabled',
},
})
interface Props {
@@ -45,7 +41,6 @@ interface Props {
flat?: boolean
showItemActions?: boolean
showEnabledForColumn?: boolean
enabledForSortDirection?: ContentCardTableSortDirection
}
const props = withDefaults(defineProps<Props>(), {
@@ -59,7 +54,6 @@ const props = withDefaults(defineProps<Props>(), {
flat: false,
showItemActions: false,
showEnabledForColumn: false,
enabledForSortDirection: undefined,
})
const stickyHeaderRef = ref<HTMLElement | null>(null)
@@ -74,7 +68,6 @@ const emit = defineEmits<{
update: [id: string]
switchVersion: [id: string]
sort: [column: ContentCardTableSortColumn, direction: ContentCardTableSortDirection]
'sort-enabled-for': [direction: ContentCardTableSortDirection]
}>()
// Check if any actions are available
@@ -194,11 +187,6 @@ function handleSort(column: ContentCardTableSortColumn) {
emit('sort', column, newDirection)
}
function handleEnabledForSort() {
const newDirection: ContentCardTableSortDirection =
props.enabledForSortDirection === 'desc' ? 'asc' : 'desc'
emit('sort-enabled-for', newDirection)
}
</script>
<template>
@@ -211,8 +199,9 @@ function handleEnabledForSort() {
v-if="!hideHeader"
ref="stickyHeaderRef"
role="rowgroup"
class="sticky top-0 z-10 flex h-12 items-center justify-between gap-4 bg-surface-3 px-3"
class="sticky top-0 z-10 flex h-12 items-center bg-surface-3 px-3"
:class="[
hasEnabledForColumn ? 'gap-2' : 'justify-between gap-4',
flat || isStuck ? 'rounded-none' : 'rounded-t-[20px]',
isStuck
? 'transition-[border-radius] duration-100 border-0 border-y border-solid border-surface-4 shadow-md before:pointer-events-none before:absolute before:inset-x-0 before:-top-4 before:h-5 before:bg-surface-3'
@@ -264,22 +253,9 @@ function handleEnabledForSort() {
<div
v-if="hasEnabledForColumn"
role="columnheader"
:aria-sort="
enabledForSortDirection
? enabledForSortDirection === 'asc'
? 'ascending'
: 'descending'
: 'none'
"
class="hidden w-[200px] shrink-0 items-center gap-1.5 font-semibold text-secondary @[800px]:flex"
>
<button
type="button"
class="cursor-pointer border-0 bg-transparent p-0 font-semibold text-secondary"
@click="handleEnabledForSort"
>
{{ formatMessage(messages.enabledFor) }}
</button>
<span>{{ formatMessage(messages.enabledFor) }}</span>
<span
v-tooltip="formatMessage(messages.enabledForDescription)"
class="inline-flex size-4 cursor-help items-center justify-center"
@@ -287,15 +263,6 @@ function handleEnabledForSort() {
>
<InfoIcon class="size-4" />
</span>
<button
type="button"
class="flex cursor-pointer items-center border-0 bg-transparent p-0 text-secondary"
:aria-label="formatMessage(messages.sortEnabledFor)"
@click="handleEnabledForSort"
>
<ChevronUpIcon v-if="enabledForSortDirection === 'asc'" class="size-4" />
<ChevronDownIcon v-else class="size-4" />
</button>
</div>
<div
@@ -303,7 +270,7 @@ function handleEnabledForSort() {
:class="
hasAnyActions
? hasEnabledForColumn
? 'w-[250px] min-w-0 shrink-0'
? 'min-w-0 flex-1'
: 'flex-1 min-w-0'
: 'flex-1'
"
@@ -8,6 +8,7 @@ import {
PaintbrushIcon,
SearchIcon,
SpinnerIcon,
UnknownIcon,
} from '@modrinth/assets'
import Fuse from 'fuse.js'
import { computed, nextTick, ref, watchSyncEffect } from 'vue'
@@ -116,6 +117,11 @@ const messages = defineMessages({
id: 'content.enabled-for.label',
defaultMessage: 'Enabled for',
},
enabledForDescription: {
id: 'content.enabled-for.description',
defaultMessage:
'Choose whether this content runs on the server, is sent to players, or is disabled in both places.',
},
pleaseWait: {
id: 'content.enabled-for.please-wait',
defaultMessage: 'Please wait',
@@ -577,7 +583,8 @@ defineExpose({ show, showLoading, hide, getState, restore, updateItem, setItems
<div v-else class="@container flex-1 min-h-0 flex flex-col">
<div
class="flex h-12 shrink-0 items-center justify-between gap-4 border-0 border-b border-solid border-surface-4 bg-surface-3 px-3"
class="flex h-12 shrink-0 items-center border-0 border-b border-solid border-surface-4 bg-surface-3 px-3"
:class="props.enableEnabledFor ? 'gap-2' : 'justify-between gap-4'"
>
<div
class="flex min-w-0 items-center gap-4"
@@ -603,18 +610,23 @@ defineExpose({ show, showLoading, hide, getState, restore, updateItem, setItems
</div>
<div
v-if="props.enableEnabledFor"
class="hidden w-[200px] shrink-0 @[800px]:flex"
class="hidden w-[200px] shrink-0 items-center gap-1.5 @[800px]:flex"
>
<span class="font-semibold text-secondary">{{
formatMessage(messages.enabledFor)
}}</span>
<UnknownIcon
v-tooltip="formatMessage(messages.enabledForDescription)"
class="size-4 cursor-help text-secondary"
tabindex="0"
/>
</div>
<div
class="hidden @[800px]:flex"
:class="
showTableActions
? props.enableEnabledFor
? 'w-[250px] min-w-0 shrink-0'
? 'min-w-0 flex-1'
: 'flex-1 min-w-0'
: 'flex-1'
"
@@ -14,7 +14,6 @@ import {
OrganizationIcon,
RefreshCwIcon,
SearchIcon,
ServerIcon,
ShareIcon,
TextCursorInputIcon,
TrashIcon,
@@ -111,14 +110,6 @@ const messages = defineMessages({
id: 'content.page-layout.sort.date-added-oldest',
defaultMessage: 'Oldest first',
},
sortEnabledForDescending: {
id: 'content.page-layout.sort.enabled-for-descending',
defaultMessage: 'Enabled for (most first)',
},
sortEnabledForAscending: {
id: 'content.page-layout.sort.enabled-for-ascending',
defaultMessage: 'Enabled for (least first)',
},
filter: {
id: 'content.page-layout.filter.add',
defaultMessage: 'Filter',
@@ -193,20 +184,24 @@ type SortMode =
| 'alphabetical-desc'
| 'date-added-newest'
| 'date-added-oldest'
| 'enabled-for-desc'
| 'enabled-for-asc'
const defaultSortMode: SortMode = ctx.setEnabledFor ? 'enabled-for-desc' : 'alphabetical-asc'
const defaultSortMode: SortMode = 'alphabetical-asc'
const sortMode = ctx.filterPersistKey
? useSessionStorage<SortMode>(`content-sort:${ctx.filterPersistKey}`, defaultSortMode)
: ref<SortMode>(defaultSortMode)
const validSortModes: readonly string[] = [
'alphabetical-asc',
'alphabetical-desc',
'date-added-newest',
'date-added-oldest',
]
if (!validSortModes.includes(sortMode.value)) sortMode.value = defaultSortMode
const sortLabels: Record<SortMode, () => string> = {
'alphabetical-asc': () => formatMessage(messages.sortAlphabeticalAscending),
'alphabetical-desc': () => formatMessage(messages.sortAlphabeticalDescending),
'date-added-newest': () => formatMessage(messages.sortDateAddedNewest),
'date-added-oldest': () => formatMessage(messages.sortDateAddedOldest),
'enabled-for-desc': () => formatMessage(messages.sortEnabledForDescending),
'enabled-for-asc': () => formatMessage(messages.sortEnabledForAscending),
}
const sortOptions = computed<OverflowMenuOption[]>(() => {
@@ -237,28 +232,9 @@ const sortOptions = computed<OverflowMenuOption[]>(() => {
},
]
if (ctx.setEnabledFor) {
options.push(
{
id: 'enabled-for-desc',
label: formatMessage(messages.sortEnabledForDescending),
action: () => (sortMode.value = 'enabled-for-desc'),
},
{
id: 'enabled-for-asc',
label: formatMessage(messages.sortEnabledForAscending),
action: () => (sortMode.value = 'enabled-for-asc'),
},
)
}
return options
})
function getEnabledForRank(item: ContentItem) {
return Number(item.enabledFor?.server) * 2 + Number(item.enabledFor?.player)
}
const sortedItems = computed(() => {
const items = [...ctx.items.value]
switch (sortMode.value) {
@@ -283,16 +259,6 @@ const sortedItems = computed(() => {
const dateB = b.date_added ?? ''
return dateA.localeCompare(dateB) || a.file_name.localeCompare(b.file_name)
})
case 'enabled-for-desc':
return items.sort(
(a, b) =>
getEnabledForRank(b) - getEnabledForRank(a) || a.file_name.localeCompare(b.file_name),
)
case 'enabled-for-asc':
return items.sort(
(a, b) =>
getEnabledForRank(a) - getEnabledForRank(b) || a.file_name.localeCompare(b.file_name),
)
default:
return items.sort((a, b) => {
const nameA = a.project?.title ?? a.file_name
@@ -886,10 +852,6 @@ async function handleSetEnabledForById(
}
}
function handleEnabledForSort(direction: 'asc' | 'desc') {
sortMode.value = direction === 'asc' ? 'enabled-for-asc' : 'enabled-for-desc'
}
// Bulk updating
const confirmBulkUpdateModal = ref<InstanceType<typeof ConfirmBulkUpdateModal>>()
const pendingBulkUpdateItems = ref<ContentItem[]>([])
@@ -1091,7 +1053,6 @@ const confirmUnlinkModal = ref<InstanceType<typeof ConfirmUnlinkModal>>()
<ArrowUpZAIcon v-if="sortMode === 'alphabetical-desc'" />
<ClockArrowDownIcon v-else-if="sortMode === 'date-added-newest'" />
<ClockArrowUpIcon v-else-if="sortMode === 'date-added-oldest'" />
<ServerIcon v-else-if="sortMode.startsWith('enabled-for')" />
<ArrowDownAZIcon v-else />
{{ sortLabels[sortMode]() }}
<DropdownIcon />
@@ -1306,16 +1267,8 @@ const confirmUnlinkModal = ref<InstanceType<typeof ConfirmUnlinkModal>>()
:items="tableItems"
:show-selection="true"
:show-enabled-for-column="!!ctx.setEnabledFor"
:enabled-for-sort-direction="
sortMode === 'enabled-for-asc'
? 'asc'
: sortMode === 'enabled-for-desc'
? 'desc'
: undefined
"
@update:enabled="handleToggleEnabledById"
@update:enabled-for="handleSetEnabledForById"
@sort-enabled-for="handleEnabledForSort"
@delete="handleDeleteById"
@update="handleUpdateById"
@switch-version="handleSwitchVersionById"
@@ -116,11 +116,11 @@ const messages = defineMessages({
},
lockEnvironment: {
id: 'hosting.content.enabled-for.lock-environment',
defaultMessage: 'Lock detected environment',
defaultMessage: 'Lock',
},
unlockEnvironment: {
id: 'hosting.content.enabled-for.unlock-environment',
defaultMessage: 'Unlock environment controls',
defaultMessage: 'Unlock',
},
})