117 lines
3.1 KiB
Svelte
117 lines
3.1 KiB
Svelte
<script lang="ts">
|
|
import Check from '$lib/icons/Check.svelte';
|
|
import Edit from '$lib/icons/Edit.svelte';
|
|
import Trash from '$lib/icons/Trash.svelte';
|
|
import X from '$lib/icons/X.svelte';
|
|
import { tick } from 'svelte';
|
|
|
|
interface ListItem {
|
|
name: string;
|
|
token?: string;
|
|
}
|
|
|
|
// props, old syntax
|
|
export let list: ListItem[] = [];
|
|
export let currentName: string;
|
|
export let vorgangToken: string | null;
|
|
export let onSave: (n: string, o: string, t?: string) => unknown = () => {};
|
|
export let onDelete: ((n: string) => unknown) | null = () => {};
|
|
|
|
let localName = currentName;
|
|
let isEditing = false;
|
|
let inputRef: HTMLInputElement | null = null;
|
|
|
|
$: error = validateName(localName);
|
|
|
|
function validateName(name: string): string {
|
|
const trimmed = name?.trim() ?? '';
|
|
if (!trimmed) return 'Name darf nicht leer sein.';
|
|
if (list.some((item) => item.name === trimmed && item.name !== currentName)) {
|
|
return 'Name existiert bereits.';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
async function startEdit() {
|
|
isEditing = true;
|
|
await tick();
|
|
inputRef?.focus();
|
|
}
|
|
|
|
function cancelEdit() {
|
|
localName = currentName;
|
|
isEditing = false;
|
|
}
|
|
|
|
function commitEdit() {
|
|
if (!error && localName != currentName) onSave(localName, currentName, vorgangToken);
|
|
// restore original value
|
|
if (error) { localName = currentName }
|
|
|
|
isEditing = false;
|
|
}
|
|
|
|
function handleKeydown(event: KeyboardEvent) {
|
|
if (event.key === 'Enter') commitEdit();
|
|
if (event.key === 'Escape') cancelEdit();
|
|
}
|
|
|
|
function handleDeleteClick() {
|
|
// vorgangToken defined when deleting Vorgang, otherwise Crime
|
|
onDelete(vorgangToken || currentName);
|
|
}
|
|
</script>
|
|
|
|
<div data-testid="test-nameItemEditor" class="flex flex-col gap-1">
|
|
{#if isEditing}
|
|
<div class="flex items-center gap-1">
|
|
<input
|
|
data-testid="test-input"
|
|
bind:this={inputRef}
|
|
bind:value={localName}
|
|
onkeydown={handleKeydown}
|
|
class="flex-1 border border-gray-300 rounded px-1.5 py-0.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
/>
|
|
<button
|
|
data-testid="commit-button"
|
|
disabled={!!error || localName === currentName}
|
|
onclick={commitEdit}
|
|
class="text-gray-500 hover:text-green-600 transition disabled:opacity-40"
|
|
>
|
|
<Check class="w-4 h-4" />
|
|
</button>
|
|
<button
|
|
data-testid="cancel-button"
|
|
onclick={cancelEdit}
|
|
class="text-gray-500 hover:text-red-600 transition"
|
|
>
|
|
<X class="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
{:else}
|
|
<div class="flex items-center gap-1">
|
|
<span class="text-sm font-medium text-gray-900 truncate">{localName}</span>
|
|
<button
|
|
data-testid="edit-button"
|
|
onclick={startEdit}
|
|
class="text-gray-500 hover:text-blue-600 transition"
|
|
>
|
|
<Edit class="w-4 h-4" />
|
|
</button>
|
|
{#if onDelete}
|
|
<button
|
|
data-testid="delete-button"
|
|
onclick={handleDeleteClick}
|
|
class="text-gray-500 hover:text-red-600 transition"
|
|
>
|
|
<Trash class="w-4 h-4" />
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
|
|
{#if error}
|
|
<p class="text-xs text-red-500 mt-1">{error}</p>
|
|
{/if}
|
|
</div>
|