290 lines
5.0 KiB
Vue
290 lines
5.0 KiB
Vue
<template>
|
|
<ul
|
|
class="list nolist"
|
|
:class="{ disabled: !editing.has('socials') }"
|
|
>
|
|
<li
|
|
v-for="(social, index) in socials"
|
|
:key="`socials-${social}`"
|
|
class="list-item"
|
|
:class="{ deleted: !socials.some((listItem) => listItem.social === social.social || listItem.url === social.url) }"
|
|
>
|
|
<a
|
|
:href="getUrl(social)"
|
|
target="_blank"
|
|
class="link"
|
|
>
|
|
<Icon
|
|
v-if="social.platform && env.socials.urls[social.platform]"
|
|
:icon="iconMap[social.platform] || social.platform"
|
|
:title="social.platform"
|
|
:class="`icon-social icon-${social.platform}`"
|
|
/>
|
|
|
|
<Icon
|
|
v-else-if="social.platform"
|
|
icon="bubbles10"
|
|
:title="social.platform"
|
|
:class="`icon-social icon-${social.platform} icon-generic`"
|
|
/>
|
|
|
|
<Icon
|
|
v-else-if="social.url"
|
|
icon="sphere"
|
|
:title="social.platform"
|
|
:class="`icon-social icon-${social.platform} icon-generic`"
|
|
/>
|
|
|
|
{{ social.handle || social.url }}
|
|
</a>
|
|
|
|
<Icon
|
|
v-if="!socials.some((listItem) => listItem.social === social.social || listItem.url === social.url)"
|
|
icon="checkmark"
|
|
class="add noselect"
|
|
@click="socials = socials.concat(social)"
|
|
/>
|
|
|
|
<Icon
|
|
v-else
|
|
icon="cross2"
|
|
class="remove noselect"
|
|
@click="socials = socials.filter((listItem, listIndex) => listIndex !== index)"
|
|
/>
|
|
</li>
|
|
|
|
<li class="list-new">
|
|
<VDropdown>
|
|
<Icon
|
|
icon="plus2"
|
|
class="add noselect"
|
|
/>
|
|
|
|
<template #popper>
|
|
<form
|
|
class="new"
|
|
@submit.prevent="addSocial"
|
|
>
|
|
<div class="new-section">
|
|
<input
|
|
v-model="platform"
|
|
list="platforms"
|
|
class="input"
|
|
placeholder="Platform"
|
|
pattern="[a-z]+"
|
|
>
|
|
|
|
<datalist id="platforms">
|
|
<option value="onlyfans">OnlyFans</option>
|
|
<option value="twitter">Twitter/X</option>
|
|
<option value="instagram">Instagram</option>
|
|
<option value="pornhub">PornHub</option>
|
|
<option value="linktree">Linktree</option>
|
|
<option value="fansly">Fansly</option>
|
|
<option value="loyalfans">LoyalFans</option>
|
|
<option value="manyvids">ManyVids</option>
|
|
<option value="cashapp">Cash App</option>
|
|
</datalist>
|
|
|
|
<input
|
|
v-model="handle"
|
|
class="input"
|
|
placeholder="Handle"
|
|
pattern="[\w-]+"
|
|
:disabled="!!url"
|
|
>
|
|
</div>
|
|
|
|
<div class="new-section">
|
|
OR<input
|
|
v-model="url"
|
|
class="input"
|
|
placeholder="Website URL"
|
|
:disabled="!!handle"
|
|
>
|
|
|
|
<button
|
|
class="button"
|
|
>Add</button>
|
|
</div>
|
|
</form>
|
|
</template>
|
|
</VDropdown>
|
|
</li>
|
|
</ul>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
ref,
|
|
watch,
|
|
inject,
|
|
} from 'vue';
|
|
|
|
import formatTemplate from 'template-format';
|
|
|
|
const pageContext = inject('pageContext');
|
|
const { env } = pageContext;
|
|
|
|
const props = defineProps({
|
|
edits: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
editing: {
|
|
type: Set,
|
|
default: null,
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(['socials']);
|
|
const socials = ref(props.edits.socials);
|
|
|
|
const platform = ref('');
|
|
const handle = ref('');
|
|
const url = ref('');
|
|
|
|
watch(socials, () => emit('socials', socials));
|
|
|
|
const iconMap = {
|
|
twitter: 'twitter-x',
|
|
};
|
|
|
|
function addSocial() {
|
|
if (!handle.value && !url.value) {
|
|
return;
|
|
}
|
|
|
|
if (handle.value && !platform.value) {
|
|
return;
|
|
}
|
|
|
|
socials.value = socials.value.concat({
|
|
platform: platform.value || null,
|
|
handle: handle.value || null,
|
|
url: url.value || null,
|
|
});
|
|
|
|
emit('socials', socials.value);
|
|
|
|
platform.value = 'onlyfans';
|
|
handle.value = '';
|
|
url.value = '';
|
|
}
|
|
|
|
function getUrl(social) {
|
|
if (social.url) {
|
|
return url;
|
|
}
|
|
|
|
if (env.socials.urls[social.platform]) {
|
|
return formatTemplate(env.socials.urls[social.platform], { handle: social.handle });
|
|
}
|
|
|
|
return null;
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.list {
|
|
display: flex;
|
|
gap: .5rem;
|
|
|
|
&.disabled {
|
|
opacity: .5;
|
|
}
|
|
}
|
|
|
|
.list-item {
|
|
.icon-social {
|
|
margin-right: .5rem;
|
|
}
|
|
|
|
.icon-generic {
|
|
fill: var(--glass-strong-20);
|
|
}
|
|
|
|
&.deleted {
|
|
color: var(--glass);
|
|
text-decoration: line-through;
|
|
|
|
.icon.icon-social {
|
|
fill: var(--glass-weak-10);
|
|
}
|
|
}
|
|
}
|
|
|
|
.list-item,
|
|
.list-new {
|
|
display: inline-flex;
|
|
align-items: stretch;
|
|
border-radius: .25rem;
|
|
box-shadow: 0 0 3px var(--shadow-weak-30);
|
|
background: var(--background);
|
|
|
|
.link {
|
|
padding: .25rem 0 .25rem .5rem;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
color: inherit;
|
|
}
|
|
|
|
.icon {
|
|
height: auto;
|
|
}
|
|
|
|
.add,
|
|
.remove {
|
|
padding: 0 .3rem;
|
|
margin-left: .5rem;
|
|
border-radius: .25rem;
|
|
|
|
&:hover {
|
|
fill: var(--text-light);
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
.add {
|
|
fill: var(--success);
|
|
|
|
&:hover {
|
|
background: var(--success);
|
|
}
|
|
}
|
|
|
|
.remove {
|
|
fill: var(--error);
|
|
|
|
&:hover {
|
|
background: var(--error);
|
|
}
|
|
}
|
|
}
|
|
|
|
.list-new .add {
|
|
padding: .25rem .5rem;
|
|
background: var(--background);
|
|
margin: 0;
|
|
}
|
|
|
|
.new {
|
|
padding: .25rem;
|
|
}
|
|
|
|
.new-section {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .5rem;
|
|
padding: .25rem;
|
|
|
|
.input {
|
|
flex-grow: 1;
|
|
|
|
&:disabled {
|
|
opacity: .5;
|
|
}
|
|
}
|
|
}
|
|
</style>
|