199 lines
4.0 KiB
Vue
199 lines
4.0 KiB
Vue
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
import { useConfirm } from '../../composables/useConfirm.js'
|
||
|
||
const props = defineProps({
|
||
elements: { type: Array, required: true },
|
||
creating: { type: Boolean, default: false },
|
||
})
|
||
|
||
const emit = defineEmits(['select', 'create', 'remove'])
|
||
|
||
const query = ref('')
|
||
const { isArmed, armOrRun } = useConfirm()
|
||
|
||
// Markdown-Zeichen für Titel und Listen-Vorschau entfernen
|
||
function plain(text) {
|
||
return (text || '').replace(/```[a-z]*\n?/g, '').replace(/[`*_#]/g, '')
|
||
}
|
||
|
||
const filtered = computed(() => {
|
||
const q = query.value.trim().toLowerCase()
|
||
if (!q) return props.elements
|
||
return props.elements.filter(
|
||
(el) => el.title.toLowerCase().includes(q) || el.description.toLowerCase().includes(q),
|
||
)
|
||
})
|
||
|
||
function add() {
|
||
if (props.creating) return
|
||
emit('create', query.value.trim())
|
||
query.value = ''
|
||
}
|
||
|
||
// Inline-Bestätigung: erster Klick „Sicher?", zweiter löscht
|
||
function confirmDelete(el) {
|
||
armOrRun('el-' + el.id, () => emit('remove', el))
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="el-new">
|
||
<input
|
||
v-model="query"
|
||
placeholder="Suchen oder Stichwort…"
|
||
:disabled="creating"
|
||
@keyup.enter="add"
|
||
/>
|
||
<button :disabled="creating" title="Element per KI erstellen" @click="add">+</button>
|
||
</div>
|
||
<div v-if="creating" class="el-creating">KI erstellt Element…</div>
|
||
<ul class="el-list">
|
||
<li v-for="el in filtered" :key="el.id" @click="emit('select', el)">
|
||
<div class="el-item-main">
|
||
<span class="el-item-title">{{ plain(el.title) }}</span>
|
||
<span class="el-item-desc">{{ plain(el.description) }}</span>
|
||
</div>
|
||
<button
|
||
class="el-delete"
|
||
:class="{ armed: isArmed('el-' + el.id) }"
|
||
title="Element löschen"
|
||
@click.stop="confirmDelete(el)"
|
||
>{{ isArmed('el-' + el.id) ? 'Sicher?' : '×' }}</button>
|
||
</li>
|
||
<li v-if="!filtered.length && !creating" class="el-empty">
|
||
{{ elements.length ? 'Keine Treffer.' : 'Noch keine Elemente. Stichwort eingeben und + klicken.' }}
|
||
</li>
|
||
</ul>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.el-new {
|
||
display: flex;
|
||
gap: 6px;
|
||
padding: 0.6rem 0.75rem;
|
||
}
|
||
|
||
.el-new input {
|
||
flex: 1;
|
||
padding: 8px 10px;
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 8px;
|
||
font-size: 0.85rem;
|
||
background: var(--panel);
|
||
color: var(--text);
|
||
outline: none;
|
||
}
|
||
|
||
.el-new input:focus {
|
||
border-color: var(--accent);
|
||
}
|
||
|
||
.el-new button {
|
||
width: 38px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
background: var(--accent);
|
||
color: var(--on-accent);
|
||
font-size: 1.1rem;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.el-new button:disabled {
|
||
opacity: 0.4;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.el-creating {
|
||
padding: 0.4rem 0.75rem;
|
||
font-size: 0.78rem;
|
||
color: var(--warning);
|
||
background: var(--warning-soft);
|
||
animation: pulse 1.5s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
50% { opacity: 0.35; }
|
||
}
|
||
|
||
.el-list {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0.25rem 0;
|
||
}
|
||
|
||
.el-list li {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 0.5rem 0.75rem;
|
||
cursor: pointer;
|
||
transition: background 0.15s;
|
||
}
|
||
|
||
.el-list li:hover {
|
||
background: var(--panel-soft);
|
||
}
|
||
|
||
.el-item-main {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.el-item-title {
|
||
display: block;
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
color: var(--text);
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.el-item-desc {
|
||
display: block;
|
||
font-size: 0.75rem;
|
||
color: var(--text-faint);
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.el-delete {
|
||
border: none;
|
||
background: none;
|
||
color: var(--danger);
|
||
font-size: 1rem;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
padding: 0 2px;
|
||
visibility: hidden;
|
||
}
|
||
|
||
.el-list li:hover .el-delete {
|
||
visibility: visible;
|
||
}
|
||
|
||
.el-delete.armed {
|
||
visibility: visible;
|
||
font-size: 0.7rem;
|
||
font-weight: 700;
|
||
background: var(--danger);
|
||
color: #fff;
|
||
border-radius: 4px;
|
||
padding: 2px 6px;
|
||
}
|
||
|
||
.el-empty {
|
||
cursor: default !important;
|
||
color: var(--text-faint);
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.el-empty:hover {
|
||
background: none !important;
|
||
}
|
||
</style>
|