Upgraded dependencies, bumped to Node 24.

This commit is contained in:
2026-07-12 05:27:14 +02:00
parent 2d4786a4fd
commit d745b10d24
181 changed files with 18720 additions and 23134 deletions

View File

@@ -1,3 +1,34 @@
<script setup>
import Gender from '#/components/actors/gender.vue';
import abbreviateNumber from '#/src/utils/abbreviate-number.js';
defineProps({
actor: {
type: Object,
default: null,
},
index: {
type: Number,
default: null,
},
filters: {
type: Object,
default: null,
},
type: {
type: String,
default: 'available',
},
toggleActor: {
type: Function,
default: null,
},
});
const emit = defineEmits(['actor']);
</script>
<template>
<li
:key="`filter-actor-${actor.id}`"
@@ -44,37 +75,6 @@
</li>
</template>
<script setup>
import abbreviateNumber from '#/src/utils/abbreviate-number.js';
import Gender from '#/components/actors/gender.vue';
defineProps({
actor: {
type: Object,
default: null,
},
index: {
type: Number,
default: null,
},
filters: {
type: Object,
default: null,
},
type: {
type: String,
default: 'available',
},
toggleActor: {
type: Function,
default: null,
},
});
const emit = defineEmits(['actor']);
</script>
<style scoped>
.filter-name {
align-items: stretch;

View File

@@ -1,3 +1,79 @@
<script setup>
import { UseVirtualList } from '@vueuse/components';
import { computed, inject, ref, useId } from 'vue';
import Gender from '#/components/actors/gender.vue';
import Actor from '#/components/filters/actor.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 genderTooltipId = useId();
const sortAlphaTooltipId = useId();
const sortCountTooltipId = useId();
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>
<template>
<div class="filter actors-container">
<div
@@ -18,7 +94,7 @@
</label>
<div
v-tooltip="selectedGender ? `${selectedGender.slice(0, 1).toUpperCase()}${selectedGender.slice(1)} performers` : `All genders`"
v-tooltip="{ content: selectedGender ? `${selectedGender.slice(0, 1).toUpperCase()}${selectedGender.slice(1)} performers` : `All genders`, ariaId: genderTooltipId }"
class="filter-sort noselect"
@click="selectGender"
>
@@ -36,7 +112,7 @@
<div
v-show="order === 'name'"
v-tooltip="'Sorted alphanumerically'"
v-tooltip="{ content: 'Sorted alphanumerically', ariaId: sortAlphaTooltipId }"
class="filter-sort order noselect"
@click="order = 'count'"
>
@@ -47,7 +123,7 @@
<div
v-show="order === 'count'"
v-tooltip="'Sorted by number of scenes'"
v-tooltip="{ content: 'Sorted by number of scenes', ariaId: sortCountTooltipId }"
class="filter-sort order noselect"
@click="order = 'name'"
>
@@ -99,78 +175,6 @@
</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;

View File

@@ -1,3 +1,25 @@
<script setup>
import { format, subYears } from 'date-fns';
import { ref } from 'vue';
import RangeFilter from '#/components/filters/range.vue';
import Checkbox from '#/components/form/checkbox.vue';
const props = defineProps({
filters: {
type: Object,
default: null,
},
});
// <select> doesn't work well with :value for some reason
const emit = defineEmits(['update']);
const maxDob = format(subYears(new Date(), 18), 'yyyy-MM-dd');
const dobType = ref(props.filters.dobType);
</script>
<template>
<div class="filter">
<div class="filter-section">
@@ -53,26 +75,6 @@
</div>
</template>
<script setup>
import { ref } from 'vue';
import { format, subYears } from 'date-fns';
import Checkbox from '#/components/form/checkbox.vue';
import RangeFilter from '#/components/filters/range.vue';
const maxDob = format(subYears(new Date(), 18), 'yyyy-MM-dd');
const props = defineProps({
filters: {
type: Object,
default: null,
},
});
const dobType = ref(props.filters.dobType); // <select> doesn't work well with :value for some reason
const emit = defineEmits(['update']);
</script>
<style scoped>
.input {
width: 100%;

View File

@@ -1,3 +1,33 @@
<script setup>
import { computed } from 'vue';
import RangeFilter from '#/components/filters/range.vue';
const props = defineProps({
filters: {
type: Object,
default: null,
},
// cup range should probably not be used, because it becomes impossible to filter for larger values than the current results
cupRange: {
type: Array,
default: null,
},
});
const emit = defineEmits(['update']);
function cupRangeToCups(cupA, cupB) {
const smallestCode = cupA.charCodeAt(0);
const biggestCode = cupB.charCodeAt(0);
return Array.from({ length: biggestCode - smallestCode + 1 }, (value, index) => String.fromCharCode(smallestCode + index));
}
const naturalBoobsValues = [true, undefined, false];
const cups = computed(() => (props.cupRange ? cupRangeToCups(props.cupRange[0], props.cupRange[1]) : 'ABCDEFGHIJKZ'.split(''))); // add Z to allow for 'bigger than K' without wasting fidelity
</script>
<template>
<div class="filter">
<RangeFilter
@@ -42,33 +72,3 @@
</span>
</div>
</template>
<script setup>
import { computed } from 'vue';
import RangeFilter from '#/components/filters/range.vue';
const props = defineProps({
filters: {
type: Object,
default: null,
},
// cup range should probably not be used, because it becomes impossible to filter for larger values than the current results
cupRange: {
type: Array,
default: null,
},
});
function cupRangeToCups(cupA, cupB) {
const smallestCode = cupA.charCodeAt(0);
const biggestCode = cupB.charCodeAt(0);
return Array.from({ length: biggestCode - smallestCode + 1 }, (value, index) => String.fromCharCode(smallestCode + index));
}
const emit = defineEmits(['update']);
const naturalBoobsValues = [true, undefined, false];
const cups = computed(() => (props.cupRange ? cupRangeToCups(props.cupRange[0], props.cupRange[1]) : 'ABCDEFGHIJKZ'.split(''))); // add Z to allow for 'bigger than K' without wasting fidelity
</script>

View File

@@ -1,3 +1,80 @@
<script setup>
import { computed, inject, ref, useId } from 'vue';
import abbreviateNumber from '#/src/utils/abbreviate-number.js';
const props = defineProps({
filters: {
type: Object,
default: null,
},
channels: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(['update']);
const search = ref('');
const searchRegexp = computed(() => new RegExp(search.value, 'i'));
const order = ref('name');
const sortAlphaTooltipId = useId();
const sortCountTooltipId = useId();
const { pageProps } = inject('pageContext');
const { channel: pageChannel } = pageProps;
function sort(channelA, channelB) {
if (order.value === 'count') {
return channelB.count - channelA.count;
}
return channelA.name.localeCompare(channelB.name);
}
const entities = computed(() => {
const filteredChannels = props.channels.filter((channel) => channel.id !== pageChannel?.id
&& (searchRegexp.value.test(channel.name)
|| searchRegexp.value.test(channel.slug)
|| (channel.parent && searchRegexp.value.test(channel.parent.name))
|| (channel.parent && searchRegexp.value.test(channel.parent.slug))));
const networks = Object.values(filteredChannels.reduce((acc, channel) => {
if (!acc[channel.id] && (channel.type === 'network' || !channel.parent || channel.isIndependent)) { // network may have already been created by a child
acc[channel.id] = {
...channel,
children: [],
};
return acc;
}
if (!acc[channel.id] && channel.parent && !acc[channel.parent.id] && (channel.type === 'channel' || channel.type === 'studio')) {
acc[channel.parent.id] = {
...channel.parent,
children: [],
};
}
if (!acc[channel.id] && channel.parent && (channel.type === 'channel' || channel.type === 'studio')) {
acc[channel.parent.id].children.push(channel);
}
return acc;
}, {}))
.map((network) => ({
...network,
children: network.children?.sort(sort),
count: network.count || network.children?.reduce((acc, channel) => acc + channel.count, 0),
}))
.sort(sort)
.flatMap((network) => [network, ...(network.children || [])]);
return networks;
});
</script>
<template>
<div class="filter channels-container">
<div class="filters-sort">
@@ -14,7 +91,7 @@
<div
v-show="order === 'name'"
v-tooltip="'Sorted alphanumerically'"
v-tooltip="{ content: 'Sorted alphanumerically', ariaId: sortAlphaTooltipId }"
class="filter-sort order noselect"
@click="order = 'count'"
>
@@ -23,7 +100,7 @@
<div
v-show="order === 'count'"
v-tooltip="'Sorted by number of scenes'"
v-tooltip="{ content: 'Sorted by number of scenes', ariaId: sortCountTooltipId }"
class="filter-sort order noselect"
@click="order = 'name'"
>
@@ -77,7 +154,7 @@
v-if="filters.entity?.id === entity.id"
icon="cross2"
class="filter-remove"
@click.native.stop="emit('update', 'entity', null)"
@click.stop="emit('update', 'entity', null)"
/>
</span>
</span>
@@ -87,81 +164,6 @@
</div>
</template>
<script setup>
import { ref, computed, inject } from 'vue';
import abbreviateNumber from '#/src/utils/abbreviate-number.js';
const props = defineProps({
filters: {
type: Object,
default: null,
},
channels: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(['update']);
const search = ref('');
const searchRegexp = computed(() => new RegExp(search.value, 'i'));
const order = ref('name');
const { pageProps } = inject('pageContext');
const { channel: pageChannel } = pageProps;
function sort(channelA, channelB) {
if (order.value === 'count') {
return channelB.count - channelA.count;
}
return channelA.name.localeCompare(channelB.name);
}
const entities = computed(() => {
const filteredChannels = props.channels.filter((channel) => channel.id !== pageChannel?.id
&& (searchRegexp.value.test(channel.name)
|| searchRegexp.value.test(channel.slug)
|| (channel.parent && searchRegexp.value.test(channel.parent.name))
|| (channel.parent && searchRegexp.value.test(channel.parent.slug))));
const networks = Object.values(filteredChannels.reduce((acc, channel) => {
if (!acc[channel.id] && (channel.type === 'network' || !channel.parent || channel.isIndependent)) { // network may have already been created by a child
acc[channel.id] = {
...channel,
children: [],
};
return acc;
}
if (!acc[channel.id] && channel.parent && !acc[channel.parent.id] && (channel.type === 'channel' || channel.type === 'studio')) {
acc[channel.parent.id] = {
...channel.parent,
children: [],
};
}
if (!acc[channel.id] && channel.parent && (channel.type === 'channel' || channel.type === 'studio')) {
acc[channel.parent.id].children.push(channel);
}
return acc;
}, {}))
.map((network) => ({
...network,
children: network.children?.sort(sort),
count: network.count || network.children?.reduce((acc, channel) => acc + channel.count, 0),
}))
.sort(sort)
.flatMap((network) => [network, ...(network.children || [])]);
return networks;
});
</script>
<style scoped>
.filter-items {
max-height: 15rem;

View File

@@ -1,3 +1,18 @@
<script setup>
defineProps({
countries: {
type: Array,
default: () => [],
},
selectedCountry: {
type: String,
default: null,
},
});
const emit = defineEmits(['country']);
</script>
<template>
<ul class="countries nolist">
<li
@@ -19,27 +34,12 @@
v-if="selectedCountry === country.alpha2"
icon="cross2"
class="filter-remove"
@click.native.stop="emit('country', null)"
@click.stop="emit('country', null)"
/>
</li>
</ul>
</template>
<script setup>
defineProps({
countries: {
type: Array,
default: () => [],
},
selectedCountry: {
type: String,
default: null,
},
});
const emit = defineEmits(['country']);
</script>
<style scoped>
.countries:not(:last-child) {
border-bottom: solid 1px var(--glass-weak-30);

View File

@@ -1,3 +1,32 @@
<script setup>
import { computed, inject, ref } from 'vue';
import Countries from './countries.vue';
const props = defineProps({
filters: {
type: Object,
default: null,
},
countries: {
type: Array,
default: null,
},
});
const emit = defineEmits(['update']);
const pageContext = inject('pageContext');
const { pageProps } = pageContext;
const allCountries = computed(() => props.countries || pageProps.countries);
const countryQuery = ref('');
const topCountryAlpha2s = ['AU', 'BR', 'CZ', 'DE', 'JP', 'RU', 'GB', 'US'];
const topCountries = computed(() => topCountryAlpha2s.map((alpha2) => allCountries.value.find((country) => country.alpha2 === alpha2)).filter(Boolean));
const filteredCountries = computed(() => allCountries.value.filter((country) => new RegExp(countryQuery.value, 'i').test(country.name)));
</script>
<template>
<div
class="countries-container"
@@ -43,35 +72,6 @@
</div>
</template>
<script setup>
import { ref, computed, inject } from 'vue';
import Countries from './countries.vue';
const props = defineProps({
filters: {
type: Object,
default: null,
},
countries: {
type: Array,
default: null,
},
});
const emit = defineEmits(['update']);
const pageContext = inject('pageContext');
const { pageProps } = pageContext;
const allCountries = computed(() => props.countries || pageProps.countries);
const countryQuery = ref('');
const topCountryAlpha2s = ['AU', 'BR', 'CZ', 'DE', 'JP', 'RU', 'GB', 'US'];
const topCountries = computed(() => topCountryAlpha2s.map((alpha2) => allCountries.value.find((country) => country.alpha2 === alpha2)).filter(Boolean));
const filteredCountries = computed(() => allCountries.value.filter((country) => new RegExp(countryQuery.value, 'i').test(country.name)));
</script>
<style scoped>
.countries-container {
border-bottom: solid 1px var(--shadow-weak-30);

View File

@@ -1,3 +1,28 @@
<script setup>
import { ref } from 'vue';
defineProps({
results: {
type: Number,
default: null,
},
loading: {
type: Boolean,
default: false,
},
});
// desktop defaults to open, compact defaults to closed
// we can't measure viewframe in SSR, so use separate toggles
const showFilters = ref(true);
const showFiltersCompact = ref(false);
function toggleFilters(state) {
showFilters.value = state ?? !showFilters.value;
showFiltersCompact.value = state ?? !showFiltersCompact.value;
}
</script>
<template>
<div class="filters-frame">
<div
@@ -47,31 +72,6 @@
</div>
</template>
<script setup>
import { ref } from 'vue';
defineProps({
results: {
type: Number,
default: null,
},
loading: {
type: Boolean,
default: false,
},
});
// desktop defaults to open, compact defaults to closed
// we can't measure viewframe in SSR, so use separate toggles
const showFilters = ref(true);
const showFiltersCompact = ref(false);
function toggleFilters(state) {
showFilters.value = state ?? !showFilters.value;
showFiltersCompact.value = state ?? !showFiltersCompact.value;
}
</script>
<style>
.filter {
padding: .5rem;

View File

@@ -1,3 +1,16 @@
<script setup>
import Gender from '#/components/actors/gender.vue';
defineProps({
filters: {
type: Object,
default: null,
},
});
const emit = defineEmits(['update']);
</script>
<template>
<ul class="filter genders nolist">
<li>
@@ -47,19 +60,6 @@
</ul>
</template>
<script setup>
import Gender from '#/components/actors/gender.vue';
defineProps({
filters: {
type: Object,
default: null,
},
});
const emit = defineEmits(['update']);
</script>
<style scoped>
.genders {
display: flex;

View File

@@ -1,3 +1,16 @@
<script setup>
import RangeFilter from '#/components/filters/range.vue';
defineProps({
filters: {
type: Object,
default: null,
},
});
const emit = defineEmits(['update']);
</script>
<template>
<div class="filter">
<RangeFilter
@@ -31,16 +44,3 @@
</RangeFilter>
</div>
</template>
<script setup>
import RangeFilter from '#/components/filters/range.vue';
defineProps({
filters: {
type: Object,
default: null,
},
});
const emit = defineEmits(['update']);
</script>

View File

@@ -1,39 +1,3 @@
<template>
<div class="filter-section">
<label class="filter-label noselect">
<span class="label">
<Checkbox
:checked="!disabled"
class="checkbox"
@change="(checked) => $emit('enable', checked)"
/>{{ label }}
</span>
<span
v-if="!disabled"
class="label-values"
>{{ value[0] }} - {{ value[1] }}<template v-if="unit">&nbsp;{{ unit }}</template></span>
</label>
<span class="filter-split">
<Range
:min="min"
:max="max"
:value="value"
:values="values"
:disabled="disabled"
:allow-enable="allowEnable"
@enable="emit('enable', true)"
@input="(range) => emit('input', range)"
@change="(range) => emit('change', range)"
>
<template #start><slot name="start" /></template>
<template #end><slot name="end" /></template>
</Range>
</span>
</div>
</template>
<script setup>
import Checkbox from '../form/checkbox.vue';
import Range from '../form/range.vue';
@@ -75,3 +39,39 @@ defineProps({
const emit = defineEmits(['change', 'input', 'enable']);
</script>
<template>
<div class="filter-section">
<label class="filter-label noselect">
<span class="label">
<Checkbox
:checked="!disabled"
class="checkbox"
@change="(checked) => $emit('enable', checked)"
/>{{ label }}
</span>
<span
v-if="!disabled"
class="label-values"
>{{ value[0] }} - {{ value[1] }}<template v-if="unit">&nbsp;{{ unit }}</template></span>
</label>
<span class="filter-split">
<Range
:min="min"
:max="max"
:value="value"
:values="values"
:disabled="disabled"
:allow-enable="allowEnable"
@enable="emit('enable', true)"
@input="(range) => emit('input', range)"
@change="(range) => emit('change', range)"
>
<template #start><slot name="start" /></template>
<template #end><slot name="end" /></template>
</Range>
</span>
</div>
</template>

View File

@@ -1,129 +1,5 @@
<template>
<div class="filter tags-container">
<div class="filters-sort">
<label class="filter-search">
<input
v-model="search"
type="search"
:placeholder="`Filter ${groupedTags.available.length} tags`"
class="input input-inline filters-search"
>
<Icon icon="search" />
</label>
<template v-if="isActorTagsAvailable">
<div
v-show="showActorTags"
v-tooltip="'Tags relevant to the selected actors'"
class="filter-sort order noselect"
@click="showActorTags = false"
>
<Icon icon="user-tags" />
</div>
<div
v-show="!showActorTags"
v-tooltip="'All tags'"
class="filter-sort order noselect"
@click="showActorTags = true"
>
<Icon icon="price-tags" />
</div>
</template>
<div
v-show="order === 'priority'"
v-tooltip="'Sorted by tag priority'"
class="filter-sort order noselect"
@click="order = 'count'"
>
<Icon icon="star" />
</div>
<div
v-show="order === 'count'"
v-tooltip="'Sorted by number of scenes'"
class="filter-sort order noselect"
@click="order = 'name'"
>
<Icon
icon="sort-numeric-desc"
/>
</div>
<div
v-show="order === 'name'"
v-tooltip="'Sorted alphanumerically'"
class="filter-sort order noselect"
@click="order = 'priority'"
>
<Icon
icon="sort-alpha-asc"
/>
</div>
</div>
<div
v-if="groupedTags.available.length === 0 && groupedTags.selected.length === 0"
class="filter-empty"
>No tags</div>
<template v-else>
<ul
v-for="(group, groupKey) in groupedTags"
:key="groupKey"
class="filter-items nolist"
:class="groupKey"
>
<li
v-for="(tag, index) in group"
:key="`filter-tag-${tag.id}`"
class="filter-item"
:class="{
selected: filters.tags.includes(tag.slug),
first: groupKey === 'available' && index === 0 && filters.tags.length > 0,
disabled: groupKey === 'page',
}"
@click.stop="toggleTag(tag, false)"
>
<div
class="filter-include"
@click.stop="toggleTag(tag, true)"
>
<Icon
icon="checkmark"
class="filter-add"
/>
<Icon
icon="cross2"
class="filter-remove"
/>
</div>
<span class="filter-name tag-name">
<span
class="filter-text"
:title="tag.name"
>{{ tag.name }}</span>
<span class="tag-details">
<span
v-if="tag.count"
:title="tag.count"
class="filter-count"
>{{ abbreviateNumber(tag.count) }}</span>
</span>
</span>
</li>
</ul>
</template>
</div>
</template>
<script setup>
import { ref, computed, inject } from 'vue';
import { computed, inject, ref, useId } from 'vue';
import abbreviateNumber from '#/src/utils/abbreviate-number.js';
@@ -155,6 +31,11 @@ const search = ref('');
const searchRegexp = computed(() => new RegExp(search.value, 'i'));
const order = ref('priority');
const showActorTags = ref(!!pageActor);
const actorTagsTooltipId = useId();
const allTagsTooltipId = useId();
const sortPriorityTooltipId = useId();
const sortCountTooltipId = useId();
const sortAlphaTooltipId = useId();
const priorityTags = [
'anal',
@@ -236,12 +117,137 @@ function toggleTag(tag, combine) {
if (combine) {
emit('update', 'tags', props.filters.tags.concat(tag.slug));
} else {
}
else {
emit('update', 'tags', [tag.slug]);
}
}
</script>
<template>
<div class="filter tags-container">
<div class="filters-sort">
<label class="filter-search">
<input
v-model="search"
type="search"
:placeholder="`Filter ${groupedTags.available.length} tags`"
class="input input-inline filters-search"
>
<Icon icon="search" />
</label>
<template v-if="isActorTagsAvailable">
<div
v-show="showActorTags"
v-tooltip="{ content: 'Tags relevant to the selected actors', ariaId: actorTagsTooltipId }"
class="filter-sort order noselect"
@click="showActorTags = false"
>
<Icon icon="user-tags" />
</div>
<div
v-show="!showActorTags"
v-tooltip="{ content: 'All tags', ariaId: allTagsTooltipId }"
class="filter-sort order noselect"
@click="showActorTags = true"
>
<Icon icon="price-tags" />
</div>
</template>
<div
v-show="order === 'priority'"
v-tooltip="{ content: 'Sorted by tag priority', ariaId: sortPriorityTooltipId }"
class="filter-sort order noselect"
@click="order = 'count'"
>
<Icon icon="star" />
</div>
<div
v-show="order === 'count'"
v-tooltip="{ content: 'Sorted by number of scenes', ariaId: sortCountTooltipId }"
class="filter-sort order noselect"
@click="order = 'name'"
>
<Icon
icon="sort-numeric-desc"
/>
</div>
<div
v-show="order === 'name'"
v-tooltip="{ content: 'Sorted alphanumerically', ariaId: sortAlphaTooltipId }"
class="filter-sort order noselect"
@click="order = 'priority'"
>
<Icon
icon="sort-alpha-asc"
/>
</div>
</div>
<div
v-if="groupedTags.available.length === 0 && groupedTags.selected.length === 0"
class="filter-empty"
>No tags</div>
<template v-else>
<ul
v-for="(group, groupKey) in groupedTags"
:key="groupKey"
class="filter-items nolist"
:class="groupKey"
>
<li
v-for="(tag, index) in group"
:key="`filter-tag-${tag.id}`"
class="filter-item"
:class="{
selected: filters.tags.includes(tag.slug),
first: groupKey === 'available' && index === 0 && filters.tags.length > 0,
disabled: groupKey === 'page',
}"
@click.stop="toggleTag(tag, false)"
>
<div
class="filter-include"
@click.stop="toggleTag(tag, true)"
>
<Icon
icon="checkmark"
class="filter-add"
/>
<Icon
icon="cross2"
class="filter-remove"
/>
</div>
<span class="filter-name tag-name">
<span
class="filter-text"
:title="tag.name"
>{{ tag.name }}</span>
<span class="tag-details">
<span
v-if="tag.count"
:title="tag.count"
class="filter-count"
>{{ abbreviateNumber(tag.count) }}</span>
</span>
</span>
</li>
</ul>
</template>
</div>
</template>
<style scoped>
.filter-items {
max-height: 15rem;

View File

@@ -1,3 +1,7 @@
<script setup>
const emit = defineEmits(['toggle']);
</script>
<template>
<div
class="filters-toggle open"
@@ -5,10 +9,6 @@
><Icon icon="filter" /></div>
</template>
<script setup>
const emit = defineEmits(['toggle']);
</script>
<style scoped>
.filters-toggle {
min-width: 2.75rem;

View File

@@ -1,28 +1,3 @@
<template>
<div class="years-container">
<select
v-model="selected"
class="years nobar"
multiple
@change="updateYears"
>
<option
v-for="year in years"
:key="`year-${year.year}`"
class="year"
:value="year.year"
>{{ year.year }}</option>
</select>
<Icon
v-show="selected.length > 0"
icon="cross2"
class="clear"
@click="clearYears"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
@@ -50,6 +25,31 @@ function clearYears() {
}
</script>
<template>
<div class="years-container">
<select
v-model="selected"
class="years nobar"
multiple
@change="updateYears"
>
<option
v-for="year in years"
:key="`year-${year.year}`"
class="year"
:value="year.year"
>{{ year.year }}</option>
</select>
<Icon
v-show="selected.length > 0"
icon="cross2"
class="clear"
@click="clearYears"
/>
</div>
</template>
<style scoped>
.years-container {
display: flex;