<template> <div v-if="actor" class="content actor" > <div class="actor-header"> <h2 class="header-name"> <span v-if="actor.entity">{{ actor.name }} ({{ actor.entity.name }})</span> <span v-else="">{{ actor.name }}</span> <Gender :gender="actor.gender" class="header-gender" /> </h2> <li v-if="actor.aliases.length" class="bio-item" > <dfn class="bio-label">Also known as</dfn> <span>{{ actor.aliases.join(', ') }}</span> </li> <Social v-if="actor.social && actor.social.length > 0" :actor="actor" class="header-social" /> </div> <div class="content-inner actor-inner"> <div class="profile" :class="{ expanded: bioExpanded, 'with-avatar': !!actor.avatar }" > <a v-if="actor.avatar" :href="`/media/${actor.avatar.path}`" target="_blank" rel="noopener noreferrer" class="avatar-link" > <img :src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`" :title="actor.avatar.credit && `© ${actor.avatar.credit}`" class="avatar" > </a> <Expand v-if="bioExpanded" :expanded="bioExpanded" class="expand expand-dark" @expand="(state) => bioExpanded = state" /> <ul class="bio nolist"> <li v-if="actor.realName" class="bio-item" > <dfn class="bio-label"><Icon icon="vcard" />Real name</dfn> <span class="bio-value">{{ actor.realName }}</span> </li> <li v-if="actor.dateOfBirth" class="bio-item" > <dfn class="bio-label"><Icon icon="cake" />Date of birth</dfn> <span class="birthdate">{{ formatDate(actor.dateOfBirth, 'MMMM D, YYYY') }}<span v-if="!actor.dateOfDeath" class="age" >{{ actor.age }}</span></span> </li> <li v-if="actor.dateOfDeath" class="bio-item" > <dfn class="bio-label"><Icon icon="tombstone" />Date of death</dfn> <span class="birthdate">{{ formatDate(actor.dateOfDeath, 'MMMM D, YYYY') }}<span v-if="actor.ageAtDeath" class="age" >{{ actor.ageAtDeath }}</span></span> </li> <li v-if="actor.origin" class="bio-item birth" > <dfn class="bio-label"><Icon icon="home2" />Born in</dfn> <span> <span v-if="actor.origin.city" class="city" >{{ actor.origin.city }}</span><span v-if="actor.origin.state && (!actor.origin.city || (actor.origin.country && actor.origin.country.alpha2 === 'US'))" class="state" >{{ actor.origin.city ? `, ${actor.origin.state}` : actor.origin.state }}</span> <span v-if="actor.origin.country" class="country birthcountry" > <img class="flag" :src="`/img/flags/${actor.origin.country.alpha2.toLowerCase()}.svg`" >{{ actor.origin.country.alias || actor.origin.country.name }} </span> </span> </li> <li v-if="actor.residence" class="bio-item residence hideable" > <dfn class="bio-label"><Icon icon="location" />Lives in</dfn> <span> <span v-if="actor.residence.city" class="city" >{{ actor.residence.city }}</span><span v-if="actor.residence.state && actor.residence.country && actor.residence.country.alpha2 === 'US'" class="state" >{{ actor.residence.city ? `, ${actor.residence.state}` : actor.residence.state }}</span> <span v-if="actor.residence.country" class="country" > <img class="flag" :src="`/img/flags/${actor.residence.country.alpha2.toLowerCase()}.svg`" >{{ actor.residence.country.alias || actor.residence.country.name }} </span> </span> </li> <li v-if="actor.ethnicity" class="bio-item ethnicity hideable" > <dfn class="bio-label"><Icon icon="earth2" />Ethnicity</dfn> <span>{{ actor.ethnicity }}</span> </li> <li v-if="actor.bust || actor.waist || actor.hip" title="bust-waist-hip" class="bio-item figure" > <dfn class="bio-label"><Icon icon="ruler" />Figure</dfn> <span class="bio-value"> <Icon v-if="actor.naturalBoobs === false" v-tooltip="'Enhanced boobs'" icon="magic-wand2" class="enhanced" />{{ actor.bust || '??' }}{{ actor.cup || '?' }}-{{ actor.waist || '??' }}-{{ actor.hip || '??' }} </span> </li> <li v-if="actor.height" class="bio-item height" > <dfn class="bio-label"><Icon icon="height" />Height</dfn> <span> <span class="height-metric">{{ actor.height.metric }} cm</span> <span class="height-imperial">{{ actor.height.imperial }}</span> </span> </li> <li v-if="actor.weight" class="bio-item weight hideable" > <dfn class="bio-label"><Icon icon="scale" />Weight</dfn> <span> <span class="weight-metric">{{ actor.weight.metric }} kg</span> <span class="weight-imperial">{{ actor.weight.imperial }} lbs</span> </span> </li> <li v-if="actor.eyes" class="bio-item eyes hideable" > <dfn class="bio-label"><Icon icon="eye" />Eyes</dfn> <span>{{ actor.eyes }}</span> </li> <li v-if="actor.hairColor" class="bio-item hair hideable" > <dfn class="bio-label"><Icon icon="haircut" />Hair</dfn> <span><span v-if="actor.hairLength">{{ actor.hairLength }}, </span>{{ actor.hairColor }}</span> </li> <li v-if="actor.hasTattoos" class="bio-item tattoos hideable" > <dfn class="bio-label"><Icon icon="lotus" />Tattoos</dfn> <span v-if="actor.tattoos" v-tooltip="actor.tattoos" class="bio-value" >{{ actor.tattoos }}</span> <span v-else>Yes</span> </li> <li v-if="actor.hasPiercings" class="bio-item piercings hideable" > <dfn class="bio-label"><Icon icon="trophy4" />Piercings</dfn> <span v-if="actor.piercings" v-tooltip="actor.piercings" class="bio-value" >{{ actor.piercings }}</span> <span v-else>Yes</span> </li> <li class="bio-item scraped hideable">Updated {{ formatDate(actor.updatedAt, 'YYYY-MM-DD HH:mm') }}, ID: {{ actor.id }}</li> </ul> <div class="descriptions-container"> <div v-if="actor.descriptions && actor.descriptions.length > 0" class="descriptions" > <p v-for="description in actor.descriptions" :key="`description-${description.entity.id}`" class="description" > {{ description.text }} <router-link :to="`/${description.entity.type}/${description.entity.slug}`"> <img v-if="description.entity.type === 'network' || description.entity.independent" :src="`/img/logos/${description.entity.slug}/thumbs/network.png`" class="description-logo" > <img v-else :src="`/img/logos/${description.entity.parent.slug}/thumbs/${description.entity.slug}.png`" class="description-logo" > </router-link> </p> </div> </div> <Social v-if="actor.social && actor.social.length > 0" :actor="actor" class="profile-social" /> <Expand :expanded="bioExpanded" class="expand expand-dark" @expand="(state) => bioExpanded = state" /> </div> <div class="actor-content"> <Scroll v-if="actor.avatar || (actor.photos && actor.photos.length > 0)" :expanded="photosExpanded" class="scroll-light" @expand="(state) => photosExpanded = state" > <Photos :actor="actor" :class="{ expanded: photosExpanded }" /> </Scroll> <FilterBar ref="filter" :fetch-releases="fetchActor" :items-total="totalCount" :items-per-page="limit" :available-tags="actor.tags" :available-channels="actor.channels" /> <Releases :releases="releases" /> <Pagination :items-total="totalCount" :items-per-page="limit" class="pagination-top" /> </div> <Footer /> </div> </div> </template> <script> import Pagination from '../pagination/pagination.vue'; import FilterBar from '../filters/filter-bar.vue'; import Releases from '../releases/releases.vue'; import Photos from './photos.vue'; import Expand from '../expand/expand.vue'; import Scroll from '../scroll/scroll.vue'; import Gender from './gender.vue'; import Social from './social.vue'; async function fetchActor() { const { actor, releases, totalCount } = await this.$store.dispatch('fetchActorById', { actorId: Number(this.$route.params.actorId), limit: this.limit, pageNumber: Number(this.$route.params.pageNumber), range: this.$route.params.range, }); this.actor = actor; this.releases = releases; this.totalCount = totalCount; if (this.$refs.filter) { this.$refs.filter.$el.scrollIntoView(); } } function sfw() { return this.$store.state.ui.sfw; } async function route() { await this.fetchActor(); } async function mounted() { await this.fetchActor(); if (this.actor) { this.pageTitle = this.actor.name; } } export default { components: { FilterBar, Pagination, Photos, Scroll, Expand, Releases, Gender, Social, }, data() { return { actor: null, releases: null, totalCount: 0, limit: 15, pageTitle: null, bioExpanded: false, photosExpanded: false, }; }, computed: { sfw, }, watch: { $route: route, }, mounted, methods: { fetchActor, }, }; </script> <style lang="scss"> .header-gender .icon { width: 1.25rem; height: 1.25rem; } </style> <style lang="scss" scoped> @import 'theme'; .actor-header { display: flex; justify-content: space-between; align-items: center; color: var(--lighten-extreme); background: var(--profile); padding: .75rem 1rem; } .header-name { padding: 0; margin: 0; display: inline-flex; justify-content: space-between; flex-shrink: 0; } .header-gender { display: inline-block; margin: 0 0 0 .5rem; transform: translate(0, .1rem); } .header-social { overflow: hidden; white-space: nowrap; margin: 0 1rem 0 0; } .profile { background: var(--profile); color: var(--lighten-extreme); width: 100%; max-height: 18rem; display: flex; flex-direction: row; flex-shrink: 0; &.with-avatar { height: 18rem; /* profile overlaps avatar in chrome */ } .avatar-link { padding: 0 0 1rem 1rem; flex-shrink: 0; } .avatar { height: 100%; flex-shrink: 0; border: solid 3px var(--lighten-hint); margin: 0 .5rem 0 0; } } .bio { flex-grow: 1; height: 100%; display: flex; flex-direction: column; flex-wrap: wrap; box-sizing: border-box; overflow: hidden; } .bio-header { width: calc(50% - 2rem); display: flex; justify-content: space-between; align-items: center; padding: 0 .5rem .5rem 0; margin: 0 0 0 1rem; } .bio-item { width: calc(50% - 4rem); display: flex; justify-content: space-between; box-sizing: border-box; padding: .25rem 0 ; margin: 0 0 .25rem 1rem; line-height: 1.75; text-align: right; font-size: .9rem; font-weight: 600; overflow: hidden; &:not(:last-of-type) { border-bottom: solid 1px var(--lighten-hint); } } .bio-label, .bio-value { display: flex; align-items: center; } .bio-label { color: var(--lighten); margin: 0 1rem 0 0; flex-shrink: 0; font-style: normal; font-weight: 400; .icon { fill: var(--lighten); margin: -.25rem .5rem 0 0; } } .bio-value { margin: 0 0 0 2rem; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; .icon { margin: -.25rem 0 0 0; } } .flag { height: 1rem; margin: .25rem .25rem 0 0; } .bio-name { display: inline-block; padding: 0; margin: 0; } .birthdate { display: block; } .age { font-weight: bold; padding: 0 0 0 .5rem; border-left: solid 1px var(--lighten-weak); margin: 0 0 0 .5rem; } .country { display: flex; justify-content: flex-end; } .figure .bio-label .icon { margin: -.5rem .5rem 0 0; } .height-imperial, .weight-imperial { padding: 0 0 0 .5rem; border-left: solid 1px var(--lighten-weak); margin: 0 0 0 .5rem; } .enhanced.icon { fill: var(--primary); padding: 0 .5rem; transform: scaleX(-1); } .ethnicity, .hair, .eyes { text-transform: capitalize; } .scraped { color: var(--lighten-weak); font-size: .8rem; } .descriptions-container { max-width: 30rem; max-height: 100%; position: relative; display: block; flex-grow: 1; box-sizing: border-box; overflow: hidden; &::after { content: ''; width: 100%; height: 1.5rem; position: absolute; bottom: 0; background: linear-gradient(transparent, 25%, var(--profile) 75%); pointer-events: none; } } .descriptions { height: 100%; overflow: auto; scrollbar-width: none; &::-webkit-scrollbar { display: none; } } .description { margin: 0; padding: 0 1rem; border-left: solid 3px var(--lighten-hint); line-height: 1.5; font-size: .9rem; } .description-logo { display: block; width: 12rem; max-height: 1.5rem; margin: .5rem 0 1.5rem 0; object-fit: contain; object-position: 0 50%; } .actor-content { display: flex; flex-grow: 1; flex-direction: column; background: var(--background-soft); } .heading { padding: 0; margin: 0 0 1rem 0; } .photos { background: var(--background-dim); } .profile-social { display: none; } .expand { display: none; } .scroll { border-bottom: solid 1px var(--shadow-hint); } @media(max-width: $breakpoint4) { .descriptions-container { display: none; } } @media(max-width: $breakpoint3) { .profile .avatar-link { display: none; } .actor-content { flex-direction: column; } } @media(max-width: $breakpoint) { .profile { height: auto; max-height: none; flex-direction: column; &.with-avatar { height: auto; max-height: none; } &:not(.expanded) .hideable { display: none; } } .bio { width: 100%; height: auto; padding: 0 1rem; margin: 0; } .bio-item { width: 100%; margin: 0; } .expanded .bio-value { white-space: normal; } .expand { display: block; } } @media(max-width: $breakpoint0) { .header-social { display: none; } .expanded .profile-social { display: block; margin: 1rem 0 0 0; } .actor-header { padding: .5rem 1rem; } .header-name { flex-grow: 1; font-size: 1.3rem; } } </style>