Added unidentified actors and actor count filters.

This commit is contained in:
2026-11-11 22:23:50 +01:00
parent 10ac366e87
commit 76ccd3729f
24 changed files with 575 additions and 70 deletions
+20 -8
View File
@@ -16,6 +16,18 @@ import Pagination from '#/components/pagination/pagination.vue';
import { get } from '#/src/api.js';
import events from '#/src/events.js';
import navigate from '#/src/navigate.js';
import { formatRange, parseLetterRange, parseRange } from '#/utils/parse-range.js';
// sliders need both ends, fill open ends with the slider limits
function getSliderRange(rangeString, [defaultMin, defaultMax], parse = parseRange) {
const range = parse(rangeString);
return [range?.min ?? defaultMin, range?.max ?? defaultMax];
}
function formatSliderRange([min, max]) {
return formatRange({ min, max });
}
const pageContext = inject('pageContext');
const { pageProps, urlParsed, routeParams } = pageContext;
@@ -35,18 +47,18 @@ const order = ref(routeParams.order || urlParsed.search.order || 'likes.desc');
const filters = ref({
gender: urlParsed.search.gender,
ageRequired: !!urlParsed.search.age,
age: urlParsed.search.age?.split(',').map((age) => Number(age)) || [18, 100],
age: getSliderRange(urlParsed.search.age, [18, 100]),
dobRequired: !!urlParsed.search.dob,
dobType: urlParsed.search.dobt ? ({ bd: 'birthday', dob: 'dob' })[urlParsed.search.dobt] : 'birthday',
dob: urlParsed.search.dob || format(subYears(new Date(), 21), 'yyyy-MM-dd'),
country: urlParsed.search.c,
braSizeRequired: !!urlParsed.search.cup,
braSize: urlParsed.search.cup?.split(',') || ['A', 'Z'],
braSize: getSliderRange(urlParsed.search.cup, ['A', 'Z'], parseLetterRange),
naturalBoobs: urlParsed.search.nb ? urlParsed.search.nb === 'true' : undefined,
heightRequired: !!urlParsed.search.height,
height: urlParsed.search.height?.split(',').map((height) => Number(height)) || [50, 220],
height: getSliderRange(urlParsed.search.height, [50, 220]),
weightRequired: !!urlParsed.search.weight,
weight: urlParsed.search.weight?.split(',').map((weight) => Number(weight)) || [30, 200],
weight: getSliderRange(urlParsed.search.weight, [30, 200]),
avatarRequired: !!urlParsed.search.avatar,
});
@@ -85,14 +97,14 @@ async function search(options = {}) {
q: q.value || undefined,
order: order.value,
gender: filters.value.gender || undefined,
age: filters.value.ageRequired ? filters.value.age.join(',') : undefined,
age: filters.value.ageRequired ? formatSliderRange(filters.value.age) : undefined,
dob: filters.value.dobRequired ? filters.value.dob : undefined,
dobt: filters.value.dobRequired ? ({ birthday: 'bd', dob: 'dob' })[filters.value.dobType] : undefined,
cup: filters.value.braSizeRequired ? filters.value.braSize.join(',') : undefined,
cup: filters.value.braSizeRequired ? formatSliderRange(filters.value.braSize) : undefined,
c: filters.value.country || undefined,
nb: filters.value.naturalBoobs,
height: filters.value.heightRequired ? filters.value.height.join(',') : undefined,
weight: filters.value.weightRequired ? filters.value.weight.join(',') : undefined,
height: filters.value.heightRequired ? formatSliderRange(filters.value.height) : undefined,
weight: filters.value.weightRequired ? formatSliderRange(filters.value.weight) : undefined,
avatar: filters.value.avatarRequired || undefined,
stashId: pageStash?.id,
};
+58
View File
@@ -1,4 +1,5 @@
<script setup>
import { nanoid } from 'nanoid';
import { ref, useId } from 'vue';
import { get } from '#/src/api.js';
@@ -9,7 +10,12 @@ defineProps({
type: Boolean,
default: false,
},
showUnknown: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['actor']);
const dropdownId = useId();
const actors = ref([]);
@@ -49,6 +55,23 @@ function focus() {
>
<ul class="actors nolist">
<li
v-if="showUnknown"
class="actor-unknown"
>
<span class="actor-label">Unidentified</span>
<Icon
v-for="gender in ['male', 'female', 'transsexual', null]"
:key="`actor-unknown-${gender}`"
v-close-popper
:icon="gender || 'question5'"
class="actor-gender"
:class="{ [`actor-${gender || 'other'}`]: true }"
@click="emit('actor', { name: gender ? `Unknown ${gender}` : 'Unknown', gender, isUnknown: true, id: `${gender || 'other'}_${nanoid()}` })"
/>
</li>
<li
v-for="actor in actors"
:key="`actor-${actor.slug}`"
@@ -89,6 +112,41 @@ function focus() {
}
}
.actor-unknown {
display: flex;
align-items: center;
.actor-label {
padding: .25rem .5rem;
flex-grow: 1;
}
}
.actor-gender {
padding: .5rem .5rem;
&:hover {
background: var(--glass-weak-50);
cursor: pointer;
}
}
.actor-male {
fill: var(--male);
}
.actor-female {
fill: var(--female);
}
.actor-transsexual {
fill: var(--trans);
}
.actor-other {
fill: var(--glass);
}
.avatar {
position: fixed;
display: none;
+11 -1
View File
@@ -33,7 +33,7 @@ const aliasTooltipId = useId();
>
<div class="avatar-container">
<Link
:href="`/actor/${actor.id}/${actor.slug}`"
:href="actor.isUnknown ? null : `/actor/${actor.id}/${actor.slug}`"
class="avatar-link no-link"
>
<img
@@ -101,10 +101,16 @@ const aliasTooltipId = useId();
<span class="label">
<span
v-if="actor.name"
class="name ellipsis"
:style="{ 'font-size': `${Math.max(0.9 + Math.min((17 - actor.name.length), 0) * 0.06, 0.65)}rem` }"
>{{ actor.name }}</span>
<span
v-else
class="name unknown"
>Unknown {{ actor.gender }}</span>
<img
v-if="actor.entity"
v-tooltip="{ content: actor.entity.name, ariaId: entityTooltipId }"
@@ -179,6 +185,10 @@ const aliasTooltipId = useId();
.name {
padding: 0 .25rem 0 .5rem;
&.unknown {
font-weight: normal;
}
}
.favicon {