mirror of
https://github.com/modrinth/code.git
synced 2026-08-26 17:44:50 +00:00
feat: exclude project types filter (#6660)
* feat: exclude project types filter * fix merge issue
This commit is contained in:
@@ -71,83 +71,105 @@
|
||||
</template>
|
||||
<template v-else #default>
|
||||
<slot name="prefix" />
|
||||
<StyledInput
|
||||
v-if="filterType.searchable"
|
||||
:id="`search-${filterType.id}`"
|
||||
v-model="query"
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.searchPlaceholder)"
|
||||
autocomplete="off"
|
||||
clearable
|
||||
size="small"
|
||||
input-class="!bg-button-bg"
|
||||
wrapper-class="mx-2 my-1 w-[calc(100%-1rem)]"
|
||||
/>
|
||||
|
||||
<ScrollablePanel :class="{ 'h-[16rem]': scrollable }" :disable-scrolling="!scrollable">
|
||||
<div :class="innerPanelClass ? innerPanelClass : ''" class="flex flex-col gap-1">
|
||||
<template v-if="groupedOptions">
|
||||
<SearchFilterGroup
|
||||
v-for="[groupName, options] in groupedOptions"
|
||||
:key="`${filterType.id}-group-${groupName}`"
|
||||
:group-name="groupName"
|
||||
:options="options"
|
||||
:supports-negative-filter="filterType.supports_negative_filter"
|
||||
:included="isIncluded"
|
||||
:excluded="isExcluded"
|
||||
@toggle="toggleFilter"
|
||||
@toggle-exclude="toggleNegativeFilter"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<SearchFilterOption
|
||||
v-for="option in visibleOptions"
|
||||
:key="`${filterType.id}-${option}`"
|
||||
:option="option"
|
||||
:included="isIncluded(option)"
|
||||
:excluded="isExcluded(option)"
|
||||
:supports-negative-filter="filterType.supports_negative_filter"
|
||||
:class="{
|
||||
'mr-3': scrollable,
|
||||
}"
|
||||
@toggle="toggleFilter"
|
||||
@toggle-exclude="toggleNegativeFilter"
|
||||
<div
|
||||
v-if="filterType.display === 'toggle'"
|
||||
:class="innerPanelClass ? innerPanelClass : ''"
|
||||
class="flex flex-col gap-3"
|
||||
>
|
||||
<label
|
||||
v-for="option in filterType.options"
|
||||
:key="`${filterType.id}-toggle-${option.id}`"
|
||||
class="flex cursor-pointer items-center justify-between text-secondary gap-3 font-semibold"
|
||||
>
|
||||
<span class="text-sm">{{ option.formatted_name ?? option.id }}</span>
|
||||
<Toggle
|
||||
:model-value="isExcluded(option)"
|
||||
small
|
||||
class="shrink-0"
|
||||
@update:model-value="toggleNegativeFilter(option)"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<template v-if="filterType.display !== 'toggle'">
|
||||
<StyledInput
|
||||
v-if="filterType.searchable"
|
||||
:id="`search-${filterType.id}`"
|
||||
v-model="query"
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.searchPlaceholder)"
|
||||
autocomplete="off"
|
||||
clearable
|
||||
size="small"
|
||||
input-class="!bg-button-bg"
|
||||
wrapper-class="mx-2 my-1 w-[calc(100%-1rem)]"
|
||||
/>
|
||||
<ScrollablePanel :class="{ 'h-[16rem]': scrollable }" :disable-scrolling="!scrollable">
|
||||
<div :class="innerPanelClass ? innerPanelClass : ''" class="flex flex-col gap-1">
|
||||
<template v-if="groupedOptions">
|
||||
<SearchFilterGroup
|
||||
v-for="[groupName, options] in groupedOptions"
|
||||
:key="`${filterType.id}-group-${groupName}`"
|
||||
:group-name="groupName"
|
||||
:options="options"
|
||||
:supports-negative-filter="filterType.supports_negative_filter"
|
||||
:included="isIncluded"
|
||||
:excluded="isExcluded"
|
||||
@toggle="toggleFilter"
|
||||
@toggle-exclude="toggleNegativeFilter"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<SearchFilterOption
|
||||
v-for="option in visibleOptions"
|
||||
:key="`${filterType.id}-${option}`"
|
||||
:option="option"
|
||||
:included="isIncluded(option)"
|
||||
:excluded="isExcluded(option)"
|
||||
:supports-negative-filter="filterType.supports_negative_filter"
|
||||
:class="{
|
||||
'mr-3': scrollable,
|
||||
}"
|
||||
@toggle="toggleFilter"
|
||||
@toggle-exclude="toggleNegativeFilter"
|
||||
>
|
||||
<slot name="option" :filter="filterType" :option="option">
|
||||
<span
|
||||
v-if="option.icon"
|
||||
class="inline-flex items-center justify-center shrink-0 h-4 w-4"
|
||||
:style="iconStyle(option)"
|
||||
>
|
||||
<div
|
||||
v-if="typeof option.icon === 'string'"
|
||||
class="h-4 w-4"
|
||||
v-html="option.icon"
|
||||
/>
|
||||
<component :is="option.icon" v-else class="h-4 w-4" />
|
||||
</span>
|
||||
<span class="truncate text-sm" :style="iconStyle(option)">
|
||||
{{ option.formatted_name ?? option.id }}
|
||||
</span>
|
||||
</slot>
|
||||
</SearchFilterOption>
|
||||
</template>
|
||||
<button
|
||||
v-if="filterType.display === 'expandable'"
|
||||
class="flex bg-transparent text-secondary border-none cursor-pointer !w-full items-center gap-2 truncate rounded-xl px-2 py-1 text-sm font-semibold transition-all hover:text-contrast focus-visible:text-contrast active:scale-[0.98]"
|
||||
@click="showMore = !showMore"
|
||||
>
|
||||
<slot name="option" :filter="filterType" :option="option">
|
||||
<span
|
||||
v-if="option.icon"
|
||||
class="inline-flex items-center justify-center shrink-0 h-4 w-4"
|
||||
:style="iconStyle(option)"
|
||||
>
|
||||
<div
|
||||
v-if="typeof option.icon === 'string'"
|
||||
class="h-4 w-4"
|
||||
v-html="option.icon"
|
||||
/>
|
||||
<component :is="option.icon" v-else class="h-4 w-4" />
|
||||
</span>
|
||||
<span class="truncate text-sm" :style="iconStyle(option)">
|
||||
{{ option.formatted_name ?? option.id }}
|
||||
</span>
|
||||
</slot>
|
||||
</SearchFilterOption>
|
||||
</template>
|
||||
<button
|
||||
v-if="filterType.display === 'expandable'"
|
||||
class="flex bg-transparent text-secondary border-none cursor-pointer !w-full items-center gap-2 truncate rounded-xl px-2 py-1 text-sm font-semibold transition-all hover:text-contrast focus-visible:text-contrast active:scale-[0.98]"
|
||||
@click="showMore = !showMore"
|
||||
>
|
||||
<DropdownIcon
|
||||
class="h-4 w-4 transition-transform"
|
||||
:class="{ 'rotate-180': showMore }"
|
||||
/>
|
||||
<span class="truncate text-sm">
|
||||
{{ showMore ? formatMessage(messages.showFewer) : formatMessage(messages.showMore) }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</ScrollablePanel>
|
||||
<DropdownIcon
|
||||
class="h-4 w-4 transition-transform"
|
||||
:class="{ 'rotate-180': showMore }"
|
||||
/>
|
||||
<span class="truncate text-sm">
|
||||
{{
|
||||
showMore ? formatMessage(messages.showFewer) : formatMessage(messages.showMore)
|
||||
}}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</ScrollablePanel>
|
||||
</template>
|
||||
<div :class="innerPanelClass ? innerPanelClass : ''" class="empty:hidden">
|
||||
<Checkbox
|
||||
v-for="group in filterType.toggle_groups"
|
||||
@@ -191,6 +213,7 @@ import { defineMessages, useVIntl } from '../../composables/i18n'
|
||||
import type { FilterOption, FilterType, FilterValue } from '../../utils/search'
|
||||
import Accordion from '../base/Accordion.vue'
|
||||
import ButtonStyled from '../base/ButtonStyled.vue'
|
||||
import Toggle from '../base/Toggle.vue'
|
||||
import { Checkbox, ScrollablePanel, StyledInput } from '../index'
|
||||
import SearchFilterGroup from './SearchFilterGroup.vue'
|
||||
import SearchFilterOption from './SearchFilterOption.vue'
|
||||
|
||||
Reference in New Issue
Block a user