Upgraded dependencies, bumped to Node 24.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"> {{ 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"> {{ 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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user