92 lines
2.7 KiB
Vue
92 lines
2.7 KiB
Vue
<template>
|
|
<div ref="root" class="relative shrink-0">
|
|
<button
|
|
type="button"
|
|
class="flex h-7 w-7 items-center justify-center rounded-full border border-white/15 text-lg leading-none text-amber-200 hover:border-amber-300/50 hover:text-amber-100 disabled:cursor-not-allowed disabled:opacity-40"
|
|
:disabled="disabled"
|
|
:title="items.length ? 'Add a saved prompt' : 'Add snippets in Settings'"
|
|
:aria-expanded="open"
|
|
aria-haspopup="menu"
|
|
aria-label="Add saved prompt"
|
|
@click.stop="open = !open"
|
|
>
|
|
+
|
|
</button>
|
|
<div
|
|
v-if="open"
|
|
class="absolute top-full z-30 mt-1 w-72 max-w-[calc(100vw-2rem)] overflow-hidden rounded-2xl border border-white/10 bg-zinc-950 shadow-2xl"
|
|
:class="align === 'left' ? 'left-0' : 'right-0'"
|
|
role="menu"
|
|
>
|
|
<p class="px-3 pt-2 text-[10px] uppercase tracking-wide text-zinc-500">{{ heading }}</p>
|
|
<p v-if="!items.length" class="px-3 py-3 text-xs text-zinc-500">
|
|
No saved snippets yet. Add video and image lists in Settings.
|
|
</p>
|
|
<ul v-else class="max-h-64 overflow-y-auto p-1">
|
|
<li v-for="item in items" :key="item.id">
|
|
<button
|
|
type="button"
|
|
class="flex w-full flex-col items-start rounded-xl px-3 py-2 text-left hover:bg-white/5"
|
|
role="menuitem"
|
|
@click="pick(item)"
|
|
>
|
|
<span class="text-xs font-medium text-amber-100">{{ item.label }}</span>
|
|
<span class="mt-0.5 line-clamp-2 whitespace-pre-wrap text-[11px] text-zinc-500">{{ item.text }}</span>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { KeepPrompt } from '~/utils/keepPrompts'
|
|
|
|
const props = withDefaults(defineProps<{
|
|
items: KeepPrompt[]
|
|
heading?: string
|
|
disabled?: boolean
|
|
align?: 'left' | 'right'
|
|
}>(), {
|
|
heading: 'Saved prompts',
|
|
disabled: false,
|
|
align: 'right'
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
insert: [item: KeepPrompt]
|
|
}>()
|
|
|
|
const root = ref<HTMLElement | null>(null)
|
|
const open = ref(false)
|
|
|
|
function pick(item: KeepPrompt) {
|
|
emit('insert', item)
|
|
open.value = false
|
|
}
|
|
|
|
function onDocClick(event: MouseEvent) {
|
|
const target = event.target as Node | null
|
|
if (root.value && target && root.value.contains(target)) return
|
|
open.value = false
|
|
}
|
|
|
|
function onKey(event: KeyboardEvent) {
|
|
if (event.key === 'Escape') open.value = false
|
|
}
|
|
|
|
watch(() => props.disabled, (value) => {
|
|
if (value) open.value = false
|
|
})
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('click', onDocClick)
|
|
window.addEventListener('keydown', onKey)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
window.removeEventListener('click', onDocClick)
|
|
window.removeEventListener('keydown', onKey)
|
|
})
|
|
</script>
|