pp-planer/resources/js/Components/MacroPicker.vue

117 lines
4 KiB
Vue

<script setup>
import { computed, ref } from 'vue'
const props = defineProps({
macros: { type: Array, default: () => [] },
collections: { type: Array, default: () => [] },
disabled: { type: Boolean, default: false },
})
const model = defineModel({ type: Number, default: null })
const search = ref('')
const isOpen = ref(false)
const filteredMacros = computed(() => {
const q = search.value.toLowerCase()
return props.macros.filter((m) => m.name.toLowerCase().includes(q))
})
const groupedMacros = computed(() => {
const groups = {}
props.collections.forEach((c) => {
groups[c.name] = []
})
groups['Ohne Sammlung'] = []
filteredMacros.value.forEach((m) => {
const coll = props.collections.find((c) => c.macros?.some((cm) => cm.id === m.id))
const key = coll?.name ?? 'Ohne Sammlung'
if (!groups[key]) groups[key] = []
groups[key].push(m)
})
return groups
})
const selectedMacro = computed(() => props.macros.find((m) => m.id === model.value))
function select(macro) {
model.value = macro.id
search.value = ''
isOpen.value = false
}
function open() {
if (!props.disabled) isOpen.value = true
}
function close() {
setTimeout(() => {
isOpen.value = false
}, 150)
}
</script>
<template>
<div class="relative">
<div
class="flex cursor-pointer items-center gap-2 rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm"
:class="{ 'cursor-not-allowed opacity-50': disabled }"
@click="open"
data-testid="macro-picker-trigger"
>
<span
v-if="selectedMacro?.color"
class="h-4 w-4 shrink-0 rounded"
:style="{ backgroundColor: selectedMacro.color }"
/>
<span class="flex-1 truncate text-gray-700">
{{ selectedMacro ? selectedMacro.name : 'Makro auswählen...' }}
</span>
</div>
<div
v-if="isOpen"
class="absolute z-50 mt-1 w-full rounded-lg border border-gray-200 bg-white shadow-lg"
data-testid="macro-picker-dropdown"
>
<div class="border-b border-gray-100 p-2">
<input
v-model="search"
type="text"
placeholder="Makro suchen..."
class="w-full rounded border-gray-300 text-sm"
data-testid="macro-picker-search"
autofocus
@blur="close"
/>
</div>
<div class="max-h-64 overflow-y-auto">
<template v-for="(group, name) in groupedMacros" :key="name">
<div
v-if="group.length > 0"
class="bg-gray-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-gray-400"
>
{{ name }}
</div>
<button
v-for="macro in group"
:key="macro.id"
class="flex w-full items-center gap-2 px-3 py-2 text-sm transition-colors hover:bg-amber-50"
:class="macro.hidden_at ? 'text-gray-400' : 'text-gray-700'"
:data-testid="'macro-option-' + macro.id"
@click="select(macro)"
>
<span
class="h-3 w-3 shrink-0 rounded"
:style="macro.color ? { backgroundColor: macro.color } : { backgroundColor: '#ccc' }"
/>
<span class="truncate">{{ macro.name }}{{ macro.hidden_at ? ' (deaktiviert)' : '' }}</span>
</button>
</template>
<div v-if="filteredMacros.length === 0" class="px-3 py-4 text-center text-sm text-gray-400">
Kein Makro gefunden
</div>
</div>
</div>
</div>
</template>