|
| 1 | +<template> |
| 2 | + <div class="experimental-styles-within flex w-full flex-col items-center gap-2"> |
| 3 | + <ManySelect |
| 4 | + v-model="selectedPlatforms" |
| 5 | + :tooltip=" |
| 6 | + filterOptions.platform.length < 2 && !disabled ? 'No other platforms available' : undefined |
| 7 | + " |
| 8 | + :options="filterOptions.platform" |
| 9 | + :dropdown-id="`${baseId}-platform`" |
| 10 | + search |
| 11 | + show-always |
| 12 | + class="w-full" |
| 13 | + :disabled="disabled || filterOptions.platform.length < 2" |
| 14 | + :dropdown-class="'w-full'" |
| 15 | + @change="updateFilters" |
| 16 | + > |
| 17 | + <slot name="platform"> |
| 18 | + <FilterIcon class="h-5 w-5 text-secondary" /> |
| 19 | + Platform |
| 20 | + </slot> |
| 21 | + <template #option="{ option }"> |
| 22 | + {{ formatCategory(option) }} |
| 23 | + </template> |
| 24 | + <template v-if="hasAnyUnsupportedPlatforms" #footer> |
| 25 | + <Checkbox |
| 26 | + v-model="showSupportedPlatformsOnly" |
| 27 | + class="mx-1" |
| 28 | + :label="`Show ${type?.toLowerCase()} platforms only`" |
| 29 | + /> |
| 30 | + </template> |
| 31 | + </ManySelect> |
| 32 | + <ManySelect |
| 33 | + v-model="selectedGameVersions" |
| 34 | + :tooltip=" |
| 35 | + filterOptions.gameVersion.length < 2 && !disabled |
| 36 | + ? 'No other game versions available' |
| 37 | + : undefined |
| 38 | + " |
| 39 | + :options="filterOptions.gameVersion" |
| 40 | + :dropdown-id="`${baseId}-game-version`" |
| 41 | + search |
| 42 | + show-always |
| 43 | + class="w-full" |
| 44 | + :disabled="disabled || filterOptions.gameVersion.length < 2" |
| 45 | + :dropdown-class="'w-full'" |
| 46 | + @change="updateFilters" |
| 47 | + > |
| 48 | + <slot name="game-versions"> |
| 49 | + <FilterIcon class="h-5 w-5 text-secondary" /> |
| 50 | + Game versions |
| 51 | + </slot> |
| 52 | + <template v-if="hasAnySnapshots" #footer> |
| 53 | + <Checkbox v-model="showSnapshots" class="mx-1" :label="`Show all versions`" /> |
| 54 | + </template> |
| 55 | + </ManySelect> |
| 56 | + </div> |
| 57 | +</template> |
| 58 | + |
| 59 | +<script setup lang="ts"> |
| 60 | +import { FilterIcon } from "@modrinth/assets"; |
| 61 | +import { type Version, formatCategory, type GameVersionTag } from "@modrinth/utils"; |
| 62 | +import { ref, computed } from "vue"; |
| 63 | +import { useRoute } from "vue-router"; |
| 64 | +import ManySelect from "@modrinth/ui/src/components/base/ManySelect.vue"; |
| 65 | +import Checkbox from "@modrinth/ui/src/components/base/Checkbox.vue"; |
| 66 | +
|
| 67 | +export type ListedGameVersion = { |
| 68 | + name: string; |
| 69 | + release: boolean; |
| 70 | +}; |
| 71 | +
|
| 72 | +export type ListedPlatform = { |
| 73 | + name: string; |
| 74 | + isType: boolean; |
| 75 | +}; |
| 76 | +
|
| 77 | +const props = defineProps<{ |
| 78 | + versions: Version[]; |
| 79 | + gameVersions: GameVersionTag[]; |
| 80 | + listedGameVersions: ListedGameVersion[]; |
| 81 | + listedPlatforms: ListedPlatform[]; |
| 82 | + baseId?: string; |
| 83 | + type: "Mod" | "Plugin"; |
| 84 | + platformTags: { |
| 85 | + name: string; |
| 86 | + supported_project_types: string[]; |
| 87 | + }[]; |
| 88 | + disabled?: boolean; |
| 89 | +}>(); |
| 90 | +
|
| 91 | +const emit = defineEmits(["update:query"]); |
| 92 | +const route = useRoute(); |
| 93 | +
|
| 94 | +const showSnapshots = ref(false); |
| 95 | +const hasAnySnapshots = computed(() => { |
| 96 | + return props.versions.some((x) => |
| 97 | + props.gameVersions.some( |
| 98 | + (y) => y.version_type !== "release" && x.game_versions.includes(y.version), |
| 99 | + ), |
| 100 | + ); |
| 101 | +}); |
| 102 | +
|
| 103 | +const hasOnlySnapshots = computed(() => { |
| 104 | + return props.versions.every((version) => { |
| 105 | + return version.game_versions.every((gv) => { |
| 106 | + const matched = props.gameVersions.find((tag) => tag.version === gv); |
| 107 | + return matched && matched.version_type !== "release"; |
| 108 | + }); |
| 109 | + }); |
| 110 | +}); |
| 111 | +
|
| 112 | +const hasAnyUnsupportedPlatforms = computed(() => { |
| 113 | + return props.listedPlatforms.some((x) => !x.isType); |
| 114 | +}); |
| 115 | +
|
| 116 | +const hasOnlyUnsupportedPlatforms = computed(() => { |
| 117 | + return props.listedPlatforms.every((x) => !x.isType); |
| 118 | +}); |
| 119 | +
|
| 120 | +const showSupportedPlatformsOnly = ref(true); |
| 121 | +
|
| 122 | +const filterOptions = computed(() => { |
| 123 | + const filters: Record<"gameVersion" | "platform", string[]> = { |
| 124 | + gameVersion: [], |
| 125 | + platform: [], |
| 126 | + }; |
| 127 | +
|
| 128 | + filters.gameVersion = props.listedGameVersions |
| 129 | + .filter((x) => { |
| 130 | + return showSnapshots.value || hasOnlySnapshots.value ? true : x.release; |
| 131 | + }) |
| 132 | + .map((x) => x.name); |
| 133 | +
|
| 134 | + filters.platform = props.listedPlatforms |
| 135 | + .filter((x) => { |
| 136 | + return !showSupportedPlatformsOnly.value || hasOnlyUnsupportedPlatforms.value |
| 137 | + ? true |
| 138 | + : x.isType; |
| 139 | + }) |
| 140 | + .map((x) => x.name); |
| 141 | +
|
| 142 | + return filters; |
| 143 | +}); |
| 144 | +
|
| 145 | +const selectedGameVersions = ref<string[]>([]); |
| 146 | +const selectedPlatforms = ref<string[]>([]); |
| 147 | +
|
| 148 | +selectedGameVersions.value = route.query.g ? getArrayOrString(route.query.g) : []; |
| 149 | +selectedPlatforms.value = route.query.l ? getArrayOrString(route.query.l) : []; |
| 150 | +
|
| 151 | +function updateFilters() { |
| 152 | + emit("update:query", { |
| 153 | + g: selectedGameVersions.value, |
| 154 | + l: selectedPlatforms.value, |
| 155 | + }); |
| 156 | +} |
| 157 | +
|
| 158 | +defineExpose({ |
| 159 | + selectedGameVersions, |
| 160 | + selectedPlatforms, |
| 161 | +}); |
| 162 | +
|
| 163 | +function getArrayOrString(x: string | (string | null)[]): string[] { |
| 164 | + if (typeof x === "string") { |
| 165 | + return [x]; |
| 166 | + } else { |
| 167 | + return x.filter((item): item is string => item !== null); |
| 168 | + } |
| 169 | +} |
| 170 | +</script> |
| 171 | + |
| 172 | +<style></style> |
0 commit comments