Merge branch 'development' into f090_magic_strings_refactoring

This commit is contained in:
2025-09-24 12:45:17 +02:00
15 changed files with 510 additions and 146 deletions

View File

@@ -1 +1,3 @@
<p class="flex justify-center m-4">In dieser Liste sind keine Einträge vorhanden</p>
<p data-testid="empty-list" class="flex justify-center m-4">
In dieser Liste sind keine Einträge vorhanden
</p>

View File

@@ -1,90 +1,83 @@
<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;
// add other properties as needed
}
let {
list,
editedName = $bindable(),
currentName,
onSave = () => {},
onDelete = () => {}
} = $props();
let localName = $state(currentName);
let wasCancelled = $state(false);
// props, old syntax
export let list: ListItem[] = [];
export let currentName: string;
export let onSave: (n: string, o: string) => unknown = () => {};
export let onDelete: (n: string) => unknown = () => {};
let error: string = $derived(validateName(localName));
let isEditing = $state(false);
let inputRef: HTMLInputElement;
let localName = currentName;
let isEditing = false;
let inputRef: HTMLInputElement | null = null;
function validateName(name: string) {
const trimmed = name.trim();
$: error = validateName(localName);
if (!trimmed) {
return 'Name darf nicht leer sein.';
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.';
}
const duplicate = list.some(
(item: ListItem) => item.name === trimmed && item.name !== currentName
);
if (duplicate) return 'Name existiert bereits.';
return '';
}
function commitIfValid() {
if (!error && !wasCancelled && localName != currentName) {
const trimmedName: string = localName.trim();
inputRef?.blur();
isEditing = false;
onSave(trimmedName, currentName);
} else {
localName = currentName;
resetEdit();
}
}
function resetEdit() {
wasCancelled = false;
inputRef?.blur();
isEditing = false;
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault();
commitIfValid();
} else if (event.key === 'Escape') {
event.preventDefault();
localName = currentName;
resetEdit();
}
}
async function startEdit() {
isEditing = true;
await tick();
inputRef?.focus();
}
function cancelEdit() {
localName = currentName;
isEditing = false;
}
function commitEdit() {
if (!error && localName != currentName) onSave(localName, currentName);
isEditing = false;
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Enter') commitEdit();
if (event.key === 'Escape') cancelEdit();
}
function handleDeleteClick() {
onDelete(currentName);
}
</script>
<div>
<input
bind:this={inputRef}
bind:value={localName}
onblur={commitIfValid}
onkeydown={handleKeydown}
/>
<button onclick={startEdit}><Edit /></button>
<button onclick={() => onDelete(currentName)}><Trash /></button>
<div data-testid="test-nameItemEditor">
{#if isEditing}
<input
data-testid="test-input"
bind:this={inputRef}
bind:value={localName}
onkeydown={handleKeydown}
/>
<button
data-testid="commit-button"
disabled={!!error || localName === currentName}
onclick={commitEdit}><Check /></button
>
<button data-testid="cancel-button" onclick={cancelEdit}><X /></button>
{:else}
<span>{localName}</span>
<button data-testid="edit-button" onclick={startEdit}><Edit /></button>
<button data-testid="delete-button" onclick={handleDeleteClick}><Trash /></button>
{/if}
{#if error}
<p style="color: red;">{error}</p>
<p class="text-red-500">{error}</p>
{/if}
</div>