Files
Archivum/frontend/src/components/editor/LinkDialog.vue

133 lines
5.8 KiB
Vue

<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
import { gql } from '@/lib/gql'
const props = defineProps<{
isOpen: boolean;
initialUrl?: string;
initialText?: string;
}>()
const emit = defineEmits<{
(e: 'close'): void;
(e: 'confirm', data: { url: string; text: string }): void;
}>()
const textInput = ref('')
const urlInput = ref('')
const documents = ref<{slug: string, title: string}[]>([])
const loadingDocs = ref(true)
const searchQuery = ref('')
const LinkType = ref<'url' | 'document' | 'anchor'>('document')
onMounted(async () => {
try {
const data = await gql<{ documents: {slug: string, title: string}[] }>(`{ documents { slug title } }`)
documents.value = data.documents ?? []
} catch (err) {
console.warn('Could not load documents')
} finally {
loadingDocs.value = false
}
})
watch(() => props.isOpen, (open) => {
if (open) {
textInput.value = props.initialText || ''
urlInput.value = props.initialUrl || ''
if (urlInput.value.startsWith('#')) {
LinkType.value = 'anchor'
urlInput.value = urlInput.value.substring(1) // strip #
} else if (urlInput.value.startsWith('http')) {
LinkType.value = 'url'
} else {
LinkType.value = 'document'
}
}
})
import { computed } from 'vue'
const filteredDocs = computed(() => {
return documents.value.filter(d =>
d.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
d.slug.toLowerCase().includes(searchQuery.value.toLowerCase())
)
})
function confirm() {
let finalUrl = urlInput.value
if (LinkType.value === 'anchor' && !finalUrl.startsWith('#')) { // just in case
finalUrl = '#' + finalUrl
}
emit('confirm', { url: finalUrl, text: textInput.value })
emit('close')
}
function selectDoc(slug: string) {
urlInput.value = slug
confirm()
}
</script>
<template>
<div v-if="isOpen" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
<div class="bg-white dark:bg-slate-800 rounded-lg shadow-xl max-w-sm w-full overflow-hidden">
<!-- Header -->
<div class="p-4 border-b dark:border-slate-700 flex justify-between items-center bg-slate-50 dark:bg-slate-900/50">
<h3 class="font-bold text-lg text-slate-800 dark:text-slate-100">Länk...</h3>
<button @click="emit('close')" class="text-slate-500 hover:text-slate-700">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<!-- Content -->
<div class="p-4 space-y-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">Text att visa</label>
<input v-model="textInput" type="text" class="w-full px-3 py-1.5 bg-white dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-md focus:ring-1 focus:ring-indigo-500 text-sm" placeholder="Text..." />
</div>
<div>
<div class="flex border-b border-slate-200 dark:border-slate-700 mb-2">
<button @click="LinkType = 'document'" :class="LinkType === 'document' ? 'border-b-2 border-indigo-500 text-indigo-600' : 'text-slate-500'" class="px-3 py-1 text-sm font-medium">Dokument</button>
<button @click="LinkType = 'anchor'" :class="LinkType === 'anchor' ? 'border-b-2 border-indigo-500 text-indigo-600' : 'text-slate-500'" class="px-3 py-1 text-sm font-medium">Bokmärke (#)</button>
<button @click="LinkType = 'url'" :class="LinkType === 'url' ? 'border-b-2 border-indigo-500 text-indigo-600' : 'text-slate-500'" class="px-3 py-1 text-sm font-medium">Extern URL</button>
</div>
<div v-if="LinkType === 'url'">
<label class="block text-xs font-medium text-slate-500 mb-1">Webbadress</label>
<input v-model="urlInput" type="url" class="w-full px-3 py-1.5 bg-white dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-md text-sm" placeholder="https://..." />
</div>
<div v-else-if="LinkType === 'anchor'">
<label class="block text-xs font-medium text-slate-500 mb-1">ID att hoppa till (utan #)</label>
<input v-model="urlInput" type="text" class="w-full px-3 py-1.5 bg-white dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-md text-sm" placeholder="avancerat" />
</div>
<div v-else-if="LinkType === 'document'" class="h-48 flex flex-col">
<input v-model="searchQuery" type="text" class="w-full px-3 py-1.5 bg-slate-50 dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-md text-sm mb-2" placeholder="Sök dokument..." />
<div class="flex-1 overflow-y-auto border border-slate-200 dark:border-slate-700 rounded-md">
<div v-if="loadingDocs" class="p-2 text-xs text-center text-slate-400">Laddar...</div>
<button
v-else
v-for="doc in filteredDocs"
:key="doc.slug"
@click="selectDoc(doc.slug)"
class="w-full text-left px-3 py-2 text-sm hover:bg-slate-100 dark:hover:bg-slate-700 border-b border-slate-100 dark:border-slate-800 last:border-0 truncate"
>
🎵 {{ doc.title || doc.slug }}
</button>
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="p-4 border-t dark:border-slate-700 flex justify-end gap-2 bg-slate-50 dark:bg-slate-900/50">
<button @click="emit('close')" class="px-4 py-2 text-sm text-slate-600 hover:text-slate-800 dark:hover:text-slate-200">Avbryt</button>
<button v-if="LinkType !== 'document'" @click="confirm" class="px-4 py-2 text-sm bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Infoga länk</button>
</div>
</div>
</div>
</template>