<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>