133 lines
5.8 KiB
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> |