180 lines
4.2 KiB
Vue
180 lines
4.2 KiB
Vue
<template>
|
|
<div class="filter actors-container">
|
|
<div
|
|
v-if="isAggActorsLimited"
|
|
class="filter-disclaimer"
|
|
>Some actors may not be listed, apply a filter or search to narrow down results.</div>
|
|
|
|
<div class="filters-sort">
|
|
<input
|
|
v-model="search"
|
|
type="search"
|
|
:placeholder="`Filter ${availableActors.length} actors`"
|
|
class="input input-inline filters-search"
|
|
>
|
|
|
|
<div
|
|
class="filter-sort noselect"
|
|
@click="selectGender"
|
|
>
|
|
<div
|
|
v-if="!selectedGender"
|
|
class="gender-unselected"
|
|
><Icon icon="genders" /></div>
|
|
|
|
<Gender
|
|
v-else
|
|
:gender="selectedGender"
|
|
class="gender"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
v-show="order === 'name'"
|
|
class="filter-sort order noselect"
|
|
@click="order = 'count'"
|
|
>
|
|
<Icon
|
|
icon="sort-alpha-asc"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
v-show="order === 'count'"
|
|
class="filter-sort order noselect"
|
|
@click="order = 'name'"
|
|
>
|
|
<Icon
|
|
icon="sort-numeric-desc"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-if="availableActors.length === 0 && selectedActors.length === 0"
|
|
class="filter-empty"
|
|
>No actors</div>
|
|
|
|
<template v-else>
|
|
<ul
|
|
v-for="(actor, index) in selectedActors"
|
|
:key="`actor-${actor.id}`"
|
|
class="filter-items nolist"
|
|
>
|
|
<Actor
|
|
:actor="actor"
|
|
:index="index"
|
|
:filters="filters"
|
|
:toggle-actor="(actor) => toggleActor(actor, true)"
|
|
type="selected"
|
|
@actor="(actor) => toggleActor(actor, false)"
|
|
/>
|
|
</ul>
|
|
|
|
<UseVirtualList
|
|
:list="availableActors"
|
|
:options="{ itemHeight: 30 }"
|
|
style="height: 20rem;"
|
|
class="filter-items nolist"
|
|
>
|
|
<template #default="{ data: actor, index }">
|
|
<Actor
|
|
:actor="actor"
|
|
:index="index"
|
|
:filters="filters"
|
|
:toggle-actor="(actor) => toggleActor(actor, true)"
|
|
type="available"
|
|
@actor="(actor) => toggleActor(actor, false)"
|
|
/>
|
|
</template>
|
|
</UseVirtualList>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, inject } from 'vue';
|
|
import { UseVirtualList } from '@vueuse/components';
|
|
|
|
import Actor from '#/components/filters/actor.vue';
|
|
import Gender from '#/components/actors/gender.vue';
|
|
|
|
const props = defineProps({
|
|
filters: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
actors: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(['update']);
|
|
|
|
const search = ref('');
|
|
const searchRegexp = computed(() => new RegExp(search.value, 'i'));
|
|
const selectedGender = ref(null);
|
|
const order = ref('count');
|
|
|
|
const pageContext = inject('pageContext');
|
|
const pageProps = pageContext.pageProps;
|
|
const { actor: pageActor } = pageProps;
|
|
|
|
const selectedActors = computed(() => props.filters.actors.map((filterActor) => props.actors.find((actor) => actor.id === filterActor.id)).filter(Boolean));
|
|
|
|
const availableActors = computed(() => props.actors
|
|
.filter((actor) => !props.filters.actors.some((filterActor) => filterActor.id === actor.id)
|
|
&& actor.id !== pageActor?.id
|
|
&& searchRegexp.value.test(actor.name)
|
|
&& (!selectedGender.value || actor.gender === selectedGender.value))
|
|
.sort((actorA, actorB) => {
|
|
if (order.value === 'count') {
|
|
return actorB.count - actorA.count;
|
|
}
|
|
|
|
return actorA.name.localeCompare(actorB.name);
|
|
}));
|
|
|
|
const genders = computed(() => [null, ...['female', 'male', 'transsexual', 'other'].filter((gender) => props.actors.some((actor) => actor.gender === gender))]);
|
|
const isAggActorsLimited = computed(() => props.actors.length >= pageContext.env.maxAggregateSize);
|
|
|
|
function toggleActor(actor, combine) {
|
|
if (props.filters.actors.some((filterActor) => filterActor.id === actor.id)) {
|
|
emit('update', 'actors', props.filters.actors.filter((filterActor) => filterActor.id !== actor.id));
|
|
return;
|
|
}
|
|
|
|
if (combine) {
|
|
emit('update', 'actors', props.filters.actors.concat(actor));
|
|
} else {
|
|
emit('update', 'actors', [actor]);
|
|
}
|
|
}
|
|
|
|
function selectGender() {
|
|
const genderIndex = genders.value.indexOf(selectedGender.value);
|
|
|
|
if (genderIndex >= genders.value.length - 1) {
|
|
selectedGender.value = genders.value[0];
|
|
return;
|
|
}
|
|
|
|
selectedGender.value = genders.value[genderIndex + 1];
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.filter {
|
|
padding: 0;
|
|
}
|
|
|
|
.filter-item.first {
|
|
border-top: solid 1px var(--shadow-weak-30);
|
|
}
|
|
|
|
.filter-items {
|
|
max-height: unset;
|
|
}
|
|
</style>
|