traxxx-web/components/edit/socials.vue

286 lines
4.7 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
v-for="platformSlug in Object.keys(env.socials.urls)"
:key="`platform-${platformSlug}`"
:value="platformSlug"
>{{ platformSlug }}</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 = '';
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>